﻿///////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2023, ООО 1С-Софт
// Все права защищены. Эта программа и сопроводительные материалы предоставляются 
// в соответствии с условиями лицензии Attribution 4.0 International (CC BY 4.0)
// Текст лицензии доступен по ссылке:
// https://creativecommons.org/licenses/by/4.0/legalcode
///////////////////////////////////////////////////////////////////////////////////////////////////////

#Область СлужебныйПрограммныйИнтерфейс

// См. СозданиеНаОснованииПереопределяемый.ПриОпределенииОбъектовСКомандамиСозданияНаОсновании.
Процедура ПриОпределенииОбъектовСКомандамиСозданияНаОсновании(Объекты) Экспорт
	
	Объекты.Добавить(Метаданные.Справочники.Файлы);
	
КонецПроцедуры

// См. ЗапретРедактированияРеквизитовОбъектовПереопределяемый.ПриОпределенииОбъектовСЗаблокированнымиРеквизитами.
Процедура ПриОпределенииОбъектовСЗаблокированнымиРеквизитами(Объекты) Экспорт
	Объекты.Вставить(Метаданные.Справочники.УчетныеЗаписиСинхронизацииФайлов.ПолноеИмя(), "");
КонецПроцедуры

// См. ПоискИУдалениеДублей.ТипыИсключаемыеИзВозможныхДублей
Процедура ПриДобавленииТиповИсключаемыхИзВозможныхДублей(ИсключаемыеТипы) Экспорт

	ОбщегоНазначенияКлиентСервер.ДополнитьМассив(
		ИсключаемыеТипы, Метаданные.ОпределяемыеТипы.ПрисоединенныйФайл.Тип.Типы());

КонецПроцедуры

// Используется при выгрузке файлов для перехода в сервис (БТС).
//
// Параметры:
//   ОбъектФайла    - СправочникОбъект
//   ИмяНовогоФайла - Строка
//
Процедура ВыгрузитьФайл(Знач ОбъектФайла, Знач ИмяНовогоФайла) Экспорт
	
	Если ОбъектФайла.ТипХраненияФайла = Перечисления.ТипыХраненияФайлов.ВТомахНаДиске Тогда
		РаботаСФайламиВТомахСлужебный.СкопироватьФайл(ОбъектФайла.Ссылка, ИмяНовогоФайла);
	Иначе
		ДвоичныеДанныеФайла = РаботаСФайлами.ДвоичныеДанныеФайла(ОбъектФайла.Ссылка);
		Попытка
			ДвоичныеДанныеФайла.Записать(ИмяНовогоФайла);
		Исключение
			СообщениеОбОшибке = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru='Данные файла были удалены. Возможно, файл очищен как ненужный.
					|%1'"), Строка(ОбъектФайла.Ссылка));
			ВызватьИсключение СообщениеОбОшибке;
		КонецПопытки;
	КонецЕсли;
	
КонецПроцедуры

// Используется при загрузке файлов для переходе в сервис (БТС).
//
// Параметры:
//  ОбъектФайла - ОпределяемыйТип.ПрисоединенныйФайлОбъект
//  ПутьКФайлу - Строка
//
Процедура ЗагрузитьФайл(Знач ОбъектФайла, Знач ПутьКФайлу) Экспорт
	
	ДвоичныеДанные = Новый ДвоичныеДанные(ПутьКФайлу);
	ТипХраненияФайла = ТипХраненияФайла(ОбъектФайла.Размер, ОбъектФайла.Расширение);
	ОбъектФайла.ТипХраненияФайла = ТипХраненияФайла;
	ОбъектФайла.ПутьКФайлу = "";
	Если ТипХраненияФайла = Перечисления.ТипыХраненияФайлов.ВТомахНаДиске Тогда
		РаботаСФайламиВТомахСлужебный.ДобавитьФайл(ОбъектФайла, ДвоичныеДанные, , Истина);
	Иначе
		ОбъектФайла.ДополнительныеСвойства.Вставить("ДвоичныеДанныеФайла", ДвоичныеДанные);
		ОбъектФайла.ФайлХранилище = Новый ХранилищеЗначения(Неопределено);
		ОбъектФайла.Том = Справочники.ТомаХраненияФайлов.ПустаяСсылка();
		ОбъектФайла.ТипХраненияФайла = Перечисления.ТипыХраненияФайлов.ВИнформационнойБазе;
	КонецЕсли;
	
КонецПроцедуры

// Параметры:
//   Результат - Структура
//   ПрисоединенныйФайл - ОпределяемыйТип.ПрисоединенныйФайлОбъект
//   ВерсияФайла - СправочникСсылка.ВерсииФайлов
//
Процедура ЗаполнитьДополнительныеДанныеФайла(Результат, ПрисоединенныйФайл, ВерсияФайла = Неопределено) Экспорт
	
	СправочникПоддерживаетВозможностьХранитьВерсии = ОбщегоНазначения.ЕстьРеквизитОбъекта("ТекущаяВерсия", Метаданные.НайтиПоТипу(ТипЗнч(ПрисоединенныйФайл)));
	
	Если СправочникПоддерживаетВозможностьХранитьВерсии И ЗначениеЗаполнено(ПрисоединенныйФайл.ТекущаяВерсия) Тогда
		ТекущаяВерсияФайла = ПрисоединенныйФайл.ТекущаяВерсия;
	Иначе
		ТекущаяВерсияФайла = ПрисоединенныйФайл.Ссылка;
	КонецЕсли;
	
	Результат.Вставить("ТекущаяВерсия", ТекущаяВерсияФайла);
	
	Если ВерсияФайла <> Неопределено Тогда
		Результат.Вставить("Версия", ВерсияФайла);
	ИначеЕсли СправочникПоддерживаетВозможностьХранитьВерсии И ЗначениеЗаполнено(ПрисоединенныйФайл.ТекущаяВерсия) Тогда
		Результат.Вставить("Версия", ПрисоединенныйФайл.ТекущаяВерсия);
	Иначе
		Результат.Вставить("Версия", ПрисоединенныйФайл.Ссылка);
	КонецЕсли;
	
	Если ЗначениеЗаполнено(ВерсияФайла) Тогда
		ТекущаяВерсияОбъект = ВерсияФайла.ПолучитьОбъект();
		Результат.Вставить("НомерВерсии", ТекущаяВерсияОбъект.НомерВерсии);
		ТекущаяВерсияФайла = ВерсияФайла;
	Иначе
		Результат.Вставить("НомерВерсии", 0);
		ТекущаяВерсияФайла = Результат.Версия;
		ТекущаяВерсияОбъект = ПрисоединенныйФайл;
	КонецЕсли;
	
	Результат.Вставить("Наименование",                 ТекущаяВерсияОбъект.Наименование);
	Результат.Вставить("Расширение",                   ТекущаяВерсияОбъект.Расширение);
	Результат.Вставить("Размер",                       ТекущаяВерсияОбъект.Размер);
	Результат.Вставить("ДатаМодификацииУниверсальная", ТекущаяВерсияОбъект.ДатаМодификацииУниверсальная);
	Результат.Вставить("Том",                          ТекущаяВерсияОбъект.Том);
	Результат.Вставить("Автор",                        ТекущаяВерсияОбъект.Автор);
	Результат.Вставить("СтатусИзвлеченияТекста",       ТекущаяВерсияОбъект.СтатусИзвлеченияТекста);
	Результат.Вставить("ПолноеНаименованиеВерсии",     СокрЛП(ТекущаяВерсияОбъект.Наименование));
	
	СтруктураКлюча = Новый Структура("Файл", ТекущаяВерсияФайла);
	КлючЗаписи = РегистрыСведений.ДвоичныеДанныеФайлов.СоздатьКлючЗаписи(СтруктураКлюча);
	НавигационнаяСсылкаТекущейВерсии = ПолучитьНавигационнуюСсылку(КлючЗаписи, "ДвоичныеДанныеФайла");
	Результат.Вставить("НавигационнаяСсылкаТекущейВерсии", НавигационнаяСсылкаТекущейВерсии);
	
	КодировкаТекущейВерсии = РегистрыСведений.КодировкиФайлов.КодировкаВерсииФайла(ТекущаяВерсияФайла);
	Результат.Вставить("КодировкаТекущейВерсии", КодировкаТекущейВерсии);
	ТекущийПользователь = Пользователи.АвторизованныйПользователь();
	НаЧтение = Результат.Редактирует <> ТекущийПользователь;
	Результат.Вставить("НаЧтение", НаЧтение);
	
	ВРабочемКаталогеНаЧтение = Истина;
	ВРабочемКаталогеВладельца = Ложь;
	ИмяКаталога = РабочийКаталогПользователя();
	
	Если ЗначениеЗаполнено(ТекущаяВерсияФайла) Тогда
		ПолноеИмяФайлаВРабочемКаталоге = РаботаСФайламиСлужебныйВызовСервера.ПолноеИмяФайлаВРабочемКаталоге(ТекущаяВерсияФайла, ИмяКаталога, ВРабочемКаталогеНаЧтение, ВРабочемКаталогеВладельца);
	
		Результат.Вставить("ПолноеИмяФайлаВРабочемКаталоге", ПолноеИмяФайлаВРабочемКаталоге);
	КонецЕсли;
	Результат.Вставить("ВРабочемКаталогеНаЧтение", ВРабочемКаталогеНаЧтение);
	Результат.Вставить("РабочийКаталогВладельца", "");
	
	РедактируетТекущийПользователь = (Результат.Редактирует = ТекущийПользователь);
	Результат.Вставить("ФайлРедактируетТекущийПользователь", РедактируетТекущийПользователь);
	
	СтатусИзвлеченияТекстаСтрока = "НеИзвлечен";
	Если Результат.СтатусИзвлеченияТекста = Перечисления.СтатусыИзвлеченияТекстаФайлов.НеИзвлечен Тогда
		СтатусИзвлеченияТекстаСтрока = "НеИзвлечен";
	ИначеЕсли Результат.СтатусИзвлеченияТекста = Перечисления.СтатусыИзвлеченияТекстаФайлов.Извлечен Тогда
		СтатусИзвлеченияТекстаСтрока = "Извлечен";
	ИначеЕсли Результат.СтатусИзвлеченияТекста = Перечисления.СтатусыИзвлеченияТекстаФайлов.ИзвлечьНеУдалось Тогда
		СтатусИзвлеченияТекстаСтрока = "ИзвлечьНеУдалось";
	КонецЕсли;
	Результат.Вставить("СтатусИзвлеченияТекста", СтатусИзвлеченияТекстаСтрока);
	
	ПапкаДляСохранитьКак = ОбщегоНазначения.ХранилищеОбщихНастроекЗагрузить("НастройкиПрограммы", "ПапкаДляСохранитьКак");
	Результат.Вставить("ПапкаДляСохранитьКак", ПапкаДляСохранитьКак);
	
КонецПроцедуры

// Процедура добавляет настройки специфичные для подсистемы Работа с файлами.
//
// Параметры:
//  ОбщиеНастройки        - Структура - настройки общие для всех пользователей.
//  ПерсональныеНастройки - Структура - настройки различные для разных пользователей.
//  
Процедура ДобавитьНастройкиРаботыСФайлами(ОбщиеНастройки, ПерсональныеНастройки) Экспорт
	
	УстановитьОтключениеБезопасногоРежима(Истина);
	УстановитьПривилегированныйРежим(Истина);
	
	ПерсональныеНастройки.Вставить("ДействиеПоДвойномуЩелчкуМыши", ДействиеПоДвойномуЩелчкуМыши());
	ПерсональныеНастройки.Вставить("СпособСравненияВерсийФайлов",  СпособСравненияВерсийФайлов());
	
	ПерсональныеНастройки.Вставить("СпрашиватьРежимРедактированияПриОткрытииФайла",
		СпрашиватьРежимРедактированияПриОткрытииФайла());
	ПерсональныеНастройки.Вставить("ВариантОткрытияФайла", ВариантОткрытияФайла());
	
	// Устарел. Следует использовать ПользователиКлиент.ЭтоПолноправныйПользователь.
	ПерсональныеНастройки.Вставить("ЭтоПолноправныйПользователь",
		Пользователи.ЭтоПолноправныйПользователь(,, Ложь));
	
	ПоказыватьЗанятыеФайлыПриЗавершенииРаботы = ОбщегоНазначения.ХранилищеОбщихНастроекЗагрузить(
		"НастройкиПрограммы", "ПоказыватьЗанятыеФайлыПриЗавершенииРаботы");
	
	Если ПоказыватьЗанятыеФайлыПриЗавершенииРаботы = Неопределено Тогда
		ПоказыватьЗанятыеФайлыПриЗавершенииРаботы = Истина;
		
		ОбщегоНазначения.ХранилищеОбщихНастроекСохранить(
			"НастройкиПрограммы",
			"ПоказыватьЗанятыеФайлыПриЗавершенииРаботы",
			ПоказыватьЗанятыеФайлыПриЗавершенииРаботы);
	КонецЕсли;
	
	ПерсональныеНастройки.Вставить("ПоказыватьЗанятыеФайлыПриЗавершенииРаботы",
		ПоказыватьЗанятыеФайлыПриЗавершенииРаботы);
	
	ПерсональныеНастройки.Вставить("ПоказыватьКолонкуРазмер", ПолучитьПоказыватьКолонкуРазмер());
	
КонецПроцедуры

// Получает все подчиненные файлы.
//
// Параметры:
//   ВладелецФайла - ЛюбаяСсылка - владелец файла.
//
// Возвращаемое значение:
//   Массив из СправочникСсылка.Файлы - массив файлов.
//
Функция ПолучитьВсеПодчиненныеФайлы(ВладелецФайла) Экспорт
	
	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ РАЗРЕШЕННЫЕ
	|	Файлы.Ссылка КАК Ссылка
	|ИЗ
	|	Справочник.Файлы КАК Файлы
	|ГДЕ
	|	Файлы.ВладелецФайла = &ВладелецФайла";
	
	Запрос.УстановитьПараметр("ВладелецФайла", ВладелецФайла);
	
	Возврат Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Ссылка");
	
КонецФункции

// Возвращает Истина, если файл с таким расширением можно загружать.
//
// Параметры:
//  РасширениеФайла - Строка
//  ВызыватьИсключение - Булево
// 
// Возвращаемое значение:
//  Булево
//
Функция ПроверитьРасширениеФайлаДляЗагрузки(РасширениеФайла, ВызыватьИсключение = Истина) Экспорт
	
	ОбщиеНастройки = РаботаСФайламиСлужебныйПовтИсп.НастройкиРаботыСФайлами().ОбщиеНастройки;
	
	Если НЕ ОбщиеНастройки.ЗапретЗагрузкиФайловПоРасширению Тогда
		Возврат Истина;
	КонецЕсли;
	
	Если РаботаСФайламиСлужебныйКлиентСервер.РасширениеФайлаВСписке(
		ОбщиеНастройки.СписокЗапрещенныхРасширений, РасширениеФайла) Тогда
		
		Если ВызыватьИсключение Тогда
			ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Загрузка файлов с расширением ""%1"" запрещена.
				           |Обратитесь к администратору.'"),
				РасширениеФайла);
		Иначе
			Возврат Ложь;
		КонецЕсли;
	КонецЕсли;
	
	Возврат Истина;
	
КонецФункции

// Возвращает типы объектов метаданных присоединенных файлов.
//
// Возвращаемое значение:
//  ОписаниеТипов.
//
Функция ТипыПрисоединенныхФайлов() Экспорт
	Возврат Метаданные.ОпределяемыеТипы.ПрисоединенныйФайл.Тип;
КонецФункции

// Возвращает общий модуль реализующий обработчики событий:
//    ПередОбновлениемДанныхФайла;
//    ПриОбновленииДанныхФайла;
//    ПослеОбновленияДанныхФайла.
// 
// Для файла должен быть установлен тип хранения файла.
// 
// Параметры:
//  ПрисоединенныйФайл - ОпределяемыйТип.ПрисоединенныйФайл
// 
// Возвращаемое значение:
//     ОбщийМодуль
//
Функция МенеджерФайлов(ПрисоединенныйФайл) Экспорт
	Возврат МенеджерФайловПоТипу(ПрисоединенныйФайл.ТипХраненияФайла);
КонецФункции

// Возвращает общий модуль реализующий обработчики событий:
//    ПередОбновлениемДанныхФайла;
//    ПриОбновленииДанныхФайла;
//    ПослеОбновленияДанныхФайла.
// 
// Параметры:
//  ТипХраненияФайла - ПеречислениеСсылка.ТипыХраненияФайлов
// 
// Возвращаемое значение:
//     ОбщийМодуль
//
Функция МенеджерФайловПоТипу(ТипХраненияФайла) Экспорт
	Если ТипХраненияФайла = Перечисления.ТипыХраненияФайлов.ВТомахНаДиске Тогда
		Возврат РаботаСФайламиВТомахСлужебный;
	Иначе
		Возврат РаботаСФайламиСлужебный;
	КонецЕсли;
КонецФункции

// Контекст обновления файла.
// Должен инициализироваться, когда основные данные файла установлены или изменены.
// 
// Параметры:
//    ПрисоединенныйФайл - ОпределяемыйТип.ПрисоединенныйФайлОбъект - ссылка или объект файла.
//                       - ОпределяемыйТип.ПрисоединенныйФайл
//     ДанныеФайла - Строка - АдресВоВременномХранилище
//                 - ДвоичныеДанные
//    СсылкаНаФайл - ОпределяемыйТип.ПрисоединенныйФайл - если задана, то будет используется в качестве ссылки.
//    													  Используется в случаях, когда для нового файла установлена ссылка нового. 
//    ТипХраненияФайла - см. МенеджерФайловПоТипу.
// 
// Возвращаемое значение:
//  Структура - контекст обновления файла:
//   * ИзменяемыеРеквизиты - Структура - значение реквизитов, устанавливаемые перед записью присоединенного файла.
//   * ПрисоединенныйФайл - см. КонтекстОбновленияФайла.ПрисоединенныйФайл
//   * ДанныеФайла - см. КонтекстОбновленияФайла.ДанныеФайла
//   * СтарыйПутьКФайлу - Строка
//   * ПараметрыДобавленияФайла - см. РаботаСФайламиВТомахСлужебный.ПараметрыДобавленияФайла
//
Функция КонтекстОбновленияФайла(ПрисоединенныйФайл, ДанныеФайла, СсылкаНаФайл = Неопределено, ТипХраненияФайла = Неопределено) Экспорт
	Контекст = Новый Структура;
	Контекст.Вставить("ИзменяемыеРеквизиты", Новый Структура);
	Контекст.Вставить("СтарыйПутьКФайлу", "");
	Контекст.Вставить("ПараметрыДобавленияФайла", РаботаСФайламиВТомахСлужебный.ПараметрыДобавленияФайла());
	Контекст.Вставить("ЭтоНовый", Ложь);
	Контекст.Вставить("ПрисоединенныйФайл", Неопределено);
	ТипПараметраФайла = ТипЗнч(ПрисоединенныйФайл);
	Если НЕ ОбщегоНазначения.ЭтоСсылка(ТипПараметраФайла)  Тогда
		Контекст.ЭтоНовый =  ПрисоединенныйФайл.ЭтоНовый();
	КонецЕсли;
	Контекст.ПрисоединенныйФайл = ?(ЗначениеЗаполнено(СсылкаНаФайл), СсылкаНаФайл, ПрисоединенныйФайл.Ссылка);
	
	ЗаполнитьЗначенияСвойств(Контекст.ПараметрыДобавленияФайла, ПрисоединенныйФайл);
	Контекст.ПараметрыДобавленияФайла.ТипХраненияФайла = ТипХраненияФайла;
	
	Если ТипЗнч(ДанныеФайла) = Тип("Строка") И ЭтоАдресВременногоХранилища(ДанныеФайла) Тогда
		ДанныеФайла = ПолучитьИзВременногоХранилища(ДанныеФайла);
	Иначе
		// Без изменений
	КонецЕсли;
	
	Контекст.Вставить("ДанныеФайла", ДанныеФайла);
	Возврат Контекст;
КонецФункции

// Параметры:
//  Контекст - см. КонтекстОбновленияФайла
//
Процедура ПередОбновлениемДанныхФайла(Контекст) Экспорт
	Возврат; // Не используется
КонецПроцедуры

// Вызывается в транзакции модификации после записи присоединенного файла.
// 
// Параметры:
//  Контекст - см. КонтекстОбновленияФайла
//  ПрисоединенныйФайлОбъект - ОпределяемыйТип.ПрисоединенныйФайлОбъект
//
Процедура ПередЗаписьюДанныхФайла(Контекст, ПрисоединенныйФайлОбъект) Экспорт
	Возврат; // Не используется
КонецПроцедуры

// Вызывается в транзакции модификации после записи присоединенного файла.
// 
// Параметры:
//  Контекст - см. КонтекстОбновленияФайла
//  ПрисоединенныйФайл - ОпределяемыйТип.ПрисоединенныйФайл
//
Процедура ПриОбновленииДанныхФайла(Контекст, ПрисоединенныйФайл) Экспорт
	ЗаписатьФайлВИнформационнуюБазу(ПрисоединенныйФайл, Контекст.ДанныеФайла);
КонецПроцедуры

// Параметры:
//  Контекст - см. КонтекстОбновленияФайла
//  Успешно - Булево - Истина, если транзакция успешно зафиксирована.
//
Процедура ПослеОбновленияДанныхФайла(Контекст, Успешно) Экспорт
	Возврат; // Не используется
КонецПроцедуры

// Возвращает массив адресов файлов и открепленных подписей. 
// 
// Параметры:
//  МассивФайлов - Массив из ОпределяемыйТип.ПрисоединенныйФайл - ссылка на элемент справочника с файлом.
//  ИдентификаторФормы - УникальныйИдентификатор.
// 
// Возвращаемое значение:
//  Массив из Структура:
//             * Представление - Строка - имя файла.
//             * АдресВоВременномХранилище - Строка
//
Функция ПоместитьФайлыВоВременноеХранилище(МассивФайлов, ИдентификаторФормы) Экспорт
	
	Параметры = Новый Структура;
	Параметры.Вставить("МассивФайлов", МассивФайлов);
	Параметры.Вставить("ИдентификаторФормы", ИдентификаторФормы);
	
	Возврат РаботаСФайламиСлужебныйВызовСервера.ПоместитьФайлыВоВременноеХранилище(Параметры);
	
КонецФункции

// Возвращает Истина, если это элемент данных, относящийся к подсистеме РаботаСФайлами.
//
Функция ЭтоЭлементРаботаСФайлами(ЭлементДанных) Экспорт
	
	ТипЭлементаДанных = ТипЗнч(ЭлементДанных);
	Если ТипЭлементаДанных = Тип("УдалениеОбъекта") Тогда
		Возврат Ложь;
	КонецЕсли;
	
	МетаданныеЭлемента = ЭлементДанных.Метаданные();
	
	Возврат ОбщегоНазначения.ЭтоСправочник(МетаданныеЭлемента)
		И (Метаданные.ОпределяемыеТипы.ПрисоединенныйФайлОбъект.Тип.СодержитТип(ТипЭлементаДанных)
			ИЛИ (Метаданные.ОпределяемыеТипы.ПрисоединенныйФайл.Тип.СодержитТип(ТипЭлементаДанных)));
	
КонецФункции

#Область ПроцедурыОбменаДанными

// Возвращает массив справочников, выступающих в роли владельцев файлов.
//
// Возвращаемое значение:
//   Массив из ОбъектМетаданных
//
Функция СправочникиФайлов() Экспорт
	
	Результат = Новый Массив();
	
	КоллекцииМетаданных = Новый Массив();
	КоллекцииМетаданных.Добавить(Метаданные.Справочники);
	КоллекцииМетаданных.Добавить(Метаданные.Документы);
	КоллекцииМетаданных.Добавить(Метаданные.БизнесПроцессы);
	КоллекцииМетаданных.Добавить(Метаданные.Задачи);
	КоллекцииМетаданных.Добавить(Метаданные.ПланыСчетов);
	КоллекцииМетаданных.Добавить(Метаданные.ПланыОбмена);
	КоллекцииМетаданных.Добавить(Метаданные.ПланыВидовХарактеристик);
	КоллекцииМетаданных.Добавить(Метаданные.ПланыВидовРасчета);
	
	Для Каждого КоллекцияМетаданных Из КоллекцииМетаданных Цикл
		
		Для Каждого ОбъектМетаданных Из КоллекцияМетаданных Цикл
			
			МенеджерОбъекта = ОбщегоНазначения.МенеджерОбъектаПоПолномуИмени(ОбъектМетаданных.ПолноеИмя());
			ПустаяСсылка = МенеджерОбъекта.ПустаяСсылка();
			ИменаСправочниковХраненияФайлов = ИменаСправочниковХраненияФайлов(ПустаяСсылка, Истина);
			
			Для Каждого ИмяСправочникаХраненияФайлов Из ИменаСправочниковХраненияФайлов Цикл
				Результат.Добавить(Метаданные.Справочники[ИмяСправочникаХраненияФайлов.Ключ]);
			КонецЦикла;
			
		КонецЦикла;
		
	КонецЦикла;
	
	Результат.Добавить(Метаданные.Справочники.ВерсииФайлов);
	
	Возврат Результат;
	
КонецФункции

// Возвращает массив объектов метаданных, которые используются для хранения
// двоичных данных файлов в информационной базе.
//
// Возвращаемое значение:
//   Массив из ОбъектМетаданных
//
Функция ОбъектыХраненияФайловИнформационнойБазе() Экспорт
	
	Результат = Новый Массив();
	Результат.Добавить(Метаданные.РегистрыСведений.ДвоичныеДанныеФайлов);
	Возврат Результат;
	
КонецФункции

// Возвращает расширение файла.
//
// Параметры:
//  Объект - ОпределяемыйТип.ПрисоединенныйФайлОбъект
// 
// Возвращаемое значение:
//   Строка
//
Функция РасширениеФайла(Объект) Экспорт
	
	Возврат Объект.Расширение;
	
КонецФункции

// Возвращает объекты, имеющие в наличии присоединенные (средствами подсистемы "Работа с файлами") файлы.
//
// Используется совместно с функцией ПрисоединенныеФайлы.СконвертироватьФайлыВПрисоединенные().
//
// Параметры:
//  ТаблицаВладельцевФайлов - Строка - полное имя объекта метаданных,
//                            который может владеть присоединенными файлами.
//
// Возвращаемое значение:
//   Массив
//
Функция СсылкиНаОбъектыСФайлами(Знач ТаблицаВладельцевФайлов) Экспорт
	
	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ
	|	ОбъектыСФайлами.Ссылка КАК Ссылка
	|ИЗ
	|	&Таблица КАК ОбъектыСФайлами
	|ГДЕ
	|	ИСТИНА В
	|			(ВЫБРАТЬ ПЕРВЫЕ 1
	|				ИСТИНА
	|			ИЗ
	|				Справочник.Файлы КАК Файлы
	|			ГДЕ
	|				Файлы.ВладелецФайла = ОбъектыСФайлами.Ссылка)";
	
	Запрос.Текст = СтрЗаменить(Запрос.Текст, "&Таблица", ТаблицаВладельцевФайлов);
	
	Возврат Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Ссылка");
	
КонецФункции

// Проверяет право текущего пользователя
// при использовании ограничения для папки или файла.
//
// Параметры:
//   Папка - СправочникСсылка.ПапкиФайлов
//         - СправочникСсылка.Файлы - папка файлов.
//         - СправочникСсылка - владелец файлов.
//
Функция ПравоДобавленияФайловВПапку(Папка) Экспорт
	
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.УправлениеДоступом") Тогда
		МодульУправлениеДоступом = ОбщегоНазначения.ОбщийМодуль("УправлениеДоступом");
		Возврат МодульУправлениеДоступом.ЕстьПраво("ДобавлениеФайлов", Папка);
	КонецЕсли;
	
	Возврат Истина;
	
КонецФункции

// Записывает вложения в папку.
//
// Параметры:
//   ПараметрыДоставки - Структура:
//     * Папка - СправочникСсылка.ПапкиФайлов
//     * Рассылка - ЛюбаяСсылка
//     * ДатаВыполнения - Дата
//     * ДобавлятьСсылки - Строка
//     * ПредставлениеОтчетовПолучателя - Строка
//   Вложения - Соответствие из КлючИЗначение:
//     * Ключ - Строка
//     * Значение - Строка 
//
Процедура ПриВыполненииДоставкиВПапку(ПараметрыДоставки, Вложения) Экспорт
	
	// Перенос вложений в таблицу
	УстановитьПривилегированныйРежим(Истина);
	
	ТаблицаВложений = Новый ТаблицаЗначений;
	ТаблицаВложений.Колонки.Добавить("ИмяФайла",              Новый ОписаниеТипов("Строка"));
	ТаблицаВложений.Колонки.Добавить("ПолныйПутьКФайлу",      Новый ОписаниеТипов("Строка"));
	ТаблицаВложений.Колонки.Добавить("Файл",                  Новый ОписаниеТипов("Файл"));
	ТаблицаВложений.Колонки.Добавить("ФайлСсылка",            Новый ОписаниеТипов("СправочникСсылка.Файлы"));
	ТаблицаВложений.Колонки.Добавить("ИмяФайлаБезРасширения", Новый ОписаниеТипов("Строка"));
	
	УстановитьПривилегированныйРежим(Ложь);
	
	Для Каждого Вложение Из Вложения Цикл
		СтрокаТаблицы = ТаблицаВложений.Добавить();
		СтрокаТаблицы.ИмяФайла              = Вложение.Ключ;
		СтрокаТаблицы.ПолныйПутьКФайлу      = Вложение.Значение;
		СтрокаТаблицы.Файл                  = Новый Файл(СтрокаТаблицы.ПолныйПутьКФайлу);
		СтрокаТаблицы.ИмяФайлаБезРасширения = СтрокаТаблицы.Файл.ИмяБезРасширения;
	КонецЦикла;
	
	// Поиск существующих файлов
	Запрос = Новый Запрос;
	Запрос.Текст = 
	"ВЫБРАТЬ РАЗРЕШЕННЫЕ РАЗЛИЧНЫЕ
	|	Файлы.Ссылка,
	|	Файлы.Наименование
	|ИЗ
	|	Справочник.Файлы КАК Файлы
	|ГДЕ
	|	Файлы.ВладелецФайла = &ВладелецФайла
	|	И Файлы.Наименование В(&МассивИменФайлов)";
	
	Запрос.УстановитьПараметр("ВладелецФайла", ПараметрыДоставки.Папка);
	Запрос.УстановитьПараметр("МассивИменФайлов", ТаблицаВложений.ВыгрузитьКолонку("ИмяФайлаБезРасширения"));
	
	СуществующиеФайлы = Запрос.Выполнить().Выгрузить();
	Для Каждого Файл Из СуществующиеФайлы Цикл
		СтрокаТаблицы = ТаблицаВложений.Найти(Файл.Наименование, "ИмяФайлаБезРасширения");
		СтрокаТаблицы.ФайлСсылка = Файл.Ссылка;
	КонецЦикла;
	
	Комментарий = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		НСтр("ru = 'Рассылка отчетов ''%1'' от %2'"),
		ПараметрыДоставки.Рассылка,
		Формат(ПараметрыДоставки.ДатаВыполнения, "ДЛФ=DT"));
	
	Для Каждого Вложение Из ТаблицаВложений Цикл
		
		СведенияОФайле = РаботаСФайламиКлиентСервер.СведенияОФайле("ФайлСВерсией", Вложение.Файл);
		СведенияОФайле.АдресВременногоХранилищаФайла = ПоместитьВоВременноеХранилище(Новый ДвоичныеДанные(Вложение.ПолныйПутьКФайлу));
		СведенияОФайле.ИмяБезРасширения = Вложение.ИмяФайлаБезРасширения;
		СведенияОФайле.Комментарий = Комментарий;
		
		Если ЗначениеЗаполнено(Вложение.ФайлСсылка) Тогда
			ВерсияСсылка = СоздатьВерсию(Вложение.ФайлСсылка, СведенияОФайле); // @skip-check query-in-loop - по-объектная запись данных.
			ОбновитьВерсиюВФайле(Вложение.ФайлСсылка, ВерсияСсылка, СведенияОФайле.АдресВременногоХранилищаТекста);
		Иначе
			Вложение.ФайлСсылка = РаботаСФайламиСлужебныйВызовСервера.СоздатьФайлСВерсией(ПараметрыДоставки.Папка, СведенияОФайле); 
		КонецЕсли;
		
		Если ПараметрыДоставки.ДобавлятьСсылки <> "" Тогда
			ПараметрыДоставки.ПредставлениеОтчетовПолучателя = СтрЗаменить(
				ПараметрыДоставки.ПредставлениеОтчетовПолучателя,
				Вложение.ПолныйПутьКФайлу,
				ПолучитьНавигационнуюСсылкуИнформационнойБазы() + "#" + ПолучитьНавигационнуюСсылку(Вложение.ФайлСсылка));
		КонецЕсли;
		
		УдалитьИзВременногоХранилища(СведенияОФайле.АдресВременногоХранилищаФайла);
	КонецЦикла;
	
КонецПроцедуры

// Устанавливает пометку удаления всем версиям указанного файла.
Процедура ПометитьНаУдалениеВерсииФайла(Знач ФайлСсылка, Знач ВерсияИсключение) Экспорт
	
	Запрос = Новый Запрос(
		"ВЫБРАТЬ
		|	ВерсииФайлов.Ссылка КАК Ссылка
		|ИЗ
		|	Справочник.ВерсииФайлов КАК ВерсииФайлов
		|ГДЕ
		|	ВерсииФайлов.Владелец = &Владелец
		|	И НЕ ВерсииФайлов.ПометкаУдаления
		|	И ВерсииФайлов.Ссылка <> &Исключение");
	Запрос.УстановитьПараметр("Владелец", ФайлСсылка);
	Запрос.УстановитьПараметр("Исключение", ВерсияИсключение);
	
	ВыборкаВерсий = Запрос.Выполнить().Выгрузить();
	
	НачатьТранзакцию();
	Попытка
		Блокировка = Новый БлокировкаДанных;
		ЭлементБлокировки = Блокировка.Добавить("Справочник.ВерсииФайлов");
		ЭлементБлокировки.УстановитьЗначение("Владелец", ФайлСсылка);
		Блокировка.Заблокировать();
		
		Для Каждого Версия Из ВыборкаВерсий Цикл
			ВерсияОбъект = Версия.Ссылка.ПолучитьОбъект();
			ВерсияОбъект.ПометкаУдаления = Истина;
			ВерсияОбъект.ДополнительныеСвойства.Вставить("КонвертацияФайлов", Истина);
			ВерсияОбъект.Записать();
		КонецЦикла;
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

// Возвращает соответствие имен справочников и значения Булево
// для указанного владельца.
// 
// Параметры:
//  ВладелецФайлов - ЛюбаяСсылка - объект, к которому добавляется файл.
// 
Функция ИменаСправочниковХраненияФайлов(ВладелецФайлов, НеВызыватьИсключение = Ложь) Экспорт
	
	Если ТипЗнч(ВладелецФайлов) = Тип("Тип") Тогда
		ТипВладельцаФайлов = ВладелецФайлов;
	Иначе
		ТипВладельцаФайлов = ТипЗнч(ВладелецФайлов);
	КонецЕсли;
	
	СуффиксСправочника = РаботаСФайламиКлиентСервер.СуффиксСправочникаПрисоединенныеФайлы();
	
	МетаданныеВладельца = Метаданные.НайтиПоТипу(ТипВладельцаФайлов);
	ИменаСправочников = Новый Соответствие;
	Если МетаданныеВладельца <> Неопределено Тогда
		ИмяСтандартногоОсновногоСправочника = МетаданныеВладельца.Имя
			+ ?(СтрЗаканчиваетсяНа(МетаданныеВладельца.Имя, СуффиксСправочника), "", СуффиксСправочника);
			
		Если Метаданные.Справочники.Найти(ИмяСтандартногоОсновногоСправочника) <> Неопределено Тогда
			ИменаСправочников.Вставить(ИмяСтандартногоОсновногоСправочника, Истина);
		ИначеЕсли Метаданные.ОпределяемыеТипы.ВладелецФайлов.Тип.СодержитТип(ТипВладельцаФайлов) Тогда
			ИменаСправочников.Вставить(Метаданные.Справочники.Файлы.Имя, Истина);
		КонецЕсли;
		
		// Переопределение стандартного справочника хранения присоединенных файлов.
		РаботаСФайламиПереопределяемый.ПриОпределенииСправочниковХраненияФайлов(
			ТипВладельцаФайлов, ИменаСправочников);
	КонецЕсли;
	
	ОсновнойСправочникУказан = Ложь;
	Ошибки = Новый Массив;
	Ошибки.Добавить(НСтр("ru = 'Ошибка при определении имен справочников для хранения файлов.'"));
	
	Для каждого КлючИЗначение Из ИменаСправочников Цикл
		
		Если Метаданные.Справочники.Найти(КлючИЗначение.Ключ) = Неопределено Тогда
			
			Ошибки.Добавить(СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'У владельца файлов типа ""%1""
					|указан несуществующий справочник ""%2"".'"),
				Строка(ТипВладельцаФайлов), Строка(КлючИЗначение.Ключ)));
				
		ИначеЕсли Не СтрЗаканчиваетсяНа(КлючИЗначение.Ключ, СуффиксСправочника) И Не КлючИЗначение.Ключ ="Файлы" Тогда
			
			Ошибки.Добавить(СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'У владельца файлов типа ""%1""
					|указано имя справочника ""%2""
					|без обязательного постфикса ""%3"".'"),
				Строка(ТипВладельцаФайлов), Строка(КлючИЗначение.Ключ), СуффиксСправочника));
			
		ИначеЕсли КлючИЗначение.Значение = Неопределено Тогда
			ИменаСправочников.Вставить(КлючИЗначение.Ключ, Ложь);
			
		ИначеЕсли КлючИЗначение.Значение = Истина Тогда
			Если ОсновнойСправочникУказан Тогда
				Ошибки.Добавить(СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'У владельца файлов типа ""%1""
						|основной справочник указан более одного раза.'"),
					Строка(ТипВладельцаФайлов), Строка(КлючИЗначение.Ключ)));
			КонецЕсли;
			ОсновнойСправочникУказан = Истина;
		КонецЕсли;
	КонецЦикла;
	
	Если ИменаСправочников.Количество() = 0 Тогда
		
		Если НеВызыватьИсключение Тогда
			Возврат ИменаСправочников;
		КонецЕсли;
		
		Ошибки.Добавить(СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'У владельца файлов типа ""%1""
				|не имеется справочников для хранения файлов.'"),
			Строка(ТипВладельцаФайлов)));
		ВызватьИсключение СтрСоединить(Ошибки, Символы.ПС + Символы.ПС);
	КонецЕсли;
	
	Если Ошибки.Количество() > 1 Тогда
		ВызватьИсключение СтрСоединить(Ошибки, Символы.ПС + Символы.ПС);
	КонецЕсли; 
	
	Возврат ИменаСправочников;
	
КонецФункции

// Создает у объекта Получатель копии всех присоединенных файлов объекта Источник.
// Источник и Получатель должны быть объектами одного типа.
//
// Параметры:
//  Источник   - ЛюбаяСсылка - объект, имеющий присоединенные файлы для копирования.
//  Получатель - ЛюбаяСсылка - объект, к которому копируются присоединенные файлы.
//
Процедура СкопироватьПрисоединенныеФайлы(Знач Источник, Знач Получатель) Экспорт
	
	ДоступнаЭлектроннаяПодпись = Неопределено;
	МодульЭлектроннаяПодписьСлужебный = Неопределено;
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ЭлектроннаяПодпись") Тогда
		МодульЭлектроннаяПодпись = ОбщегоНазначения.ОбщийМодуль("ЭлектроннаяПодпись");
		МодульЭлектроннаяПодписьСлужебный = ОбщегоНазначения.ОбщийМодуль("ЭлектроннаяПодписьСлужебный");
	КонецЕсли;
	
	КопируемыеФайлы = ПрисоединенныеФайлыКОбъекту(Источник, Истина);
	Для Каждого КопируемыйФайл Из КопируемыеФайлы Цикл
		Если ДоступнаЭлектроннаяПодпись = Неопределено Тогда
			ДоступнаЭлектроннаяПодпись = (МодульЭлектроннаяПодписьСлужебный <> Неопределено) 
				И (МодульЭлектроннаяПодписьСлужебный.ДоступнаЭлектроннаяПодпись(ТипЗнч(КопируемыйФайл)));
		КонецЕсли;
		
		НачатьТранзакцию();
		Попытка
			МенеджерОбъекта = ОбщегоНазначения.МенеджерОбъектаПоСсылке(КопируемыйФайл);
			КопияФайла = КопируемыйФайл.Скопировать();
			КопияФайлаСсылка = МенеджерОбъекта.ПолучитьСсылку();
			КопияФайла.УстановитьСсылкуНового(КопияФайлаСсылка);
			КопияФайла.ВладелецФайла = Получатель;
			КопияФайла.Редактирует = Справочники.Пользователи.ПустаяСсылка();
			
			КопияФайла.ТекстХранилище = КопируемыйФайл.ТекстХранилище;
			КопияФайла.СтатусИзвлеченияТекста = КопируемыйФайл.СтатусИзвлеченияТекста;
			КопияФайла.ФайлХранилище = КопируемыйФайл.ФайлХранилище;
			
			ДвоичныеДанные = РаботаСФайлами.ДвоичныеДанныеФайла(КопируемыйФайл);
			
			ТипХраненияФайла = ТипХраненияФайла(КопияФайла.Размер, КопияФайла.Расширение);
			Если ТипХраненияФайла = Перечисления.ТипыХраненияФайлов.ВИнформационнойБазе Тогда
				КопияФайла.ТипХраненияФайла = ТипХраненияФайла;
				ЗаписатьФайлВИнформационнуюБазу(КопияФайлаСсылка, ДвоичныеДанные);
			Иначе
				КопияФайла.Том = Неопределено;
				КопияФайла.ПутьКФайлу = Неопределено;
				КопияФайла.ТипХраненияФайла = Неопределено;
				РаботаСФайламиВТомахСлужебный.ДобавитьФайл(КопияФайла, ДвоичныеДанные);
			КонецЕсли;
			КопияФайла.Записать();
			
			Если ДоступнаЭлектроннаяПодпись Тогда
				УстановленныеПодписи = МодульЭлектроннаяПодпись.УстановленныеПодписи(КопируемыйФайл);
				МодульЭлектроннаяПодпись.ДобавитьПодпись(КопияФайла.Ссылка, УстановленныеПодписи);
				
				СертификатыИсточника = МодульЭлектроннаяПодпись.СертификатыШифрования(КопируемыйФайл);
				МодульЭлектроннаяПодпись.ЗаписатьСертификатыШифрования(КопияФайла, СертификатыИсточника);
			КонецЕсли;
			ЗафиксироватьТранзакцию();
		Исключение
			ОтменитьТранзакцию();
			ВызватьИсключение;
		КонецПопытки;
	КонецЦикла;
	
КонецПроцедуры

// Возвращает структуру объекта файла.
//
Функция ФайлОбъект(Знач ПрисоединенныйФайл) Экспорт
	
	ФайлОбъект = Неопределено;
	МетаданныеОбъектаФайла = Метаданные.НайтиПоТипу(ТипЗнч(ПрисоединенныйФайл));
	
	// Это справочник файлов.
	Если ОбщегоНазначения.ЕстьРеквизитОбъекта("ВладелецФайла", МетаданныеОбъектаФайла) Тогда
		ЕстьТекущаяВерсия = Ложь;
		Если ОбщегоНазначения.ЕстьРеквизитОбъекта("ТекущаяВерсия", МетаданныеОбъектаФайла) Тогда
			ТекущаяВерсия = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(ПрисоединенныйФайл, "ТекущаяВерсия"); 
			ЕстьТекущаяВерсия = ЗначениеЗаполнено(ТекущаяВерсия); 
		КонецЕсли;	
		Если ЕстьТекущаяВерсия Тогда // С возможностью хранить версии
			ИменаРеквизитов = "Ссылка,ТипХраненияФайла,Наименование,Расширение,Том,ПутьКФайлу,ПометкаУдаления";
			Если МетаданныеОбъектаФайла.Иерархический Тогда
				ИменаРеквизитов = ИменаРеквизитов + "," + "ЭтоГруппа";
			КонецЕсли;
			ФайлОбъект = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(ТекущаяВерсия, ИменаРеквизитов);
			ФайлОбъект.Вставить("ВладелецФайла", ОбщегоНазначения.ЗначениеРеквизитаОбъекта(ПрисоединенныйФайл, "ВладелецФайла"));
			Если Не МетаданныеОбъектаФайла.Иерархический Тогда
				ФайлОбъект.Вставить("ЭтоГруппа", Ложь);
			КонецЕсли;
		Иначе // Без возможности хранить версии
			ИменаРеквизитов = "Ссылка,ТипХраненияФайла,ВладелецФайла,Наименование,Расширение,Том,ПутьКФайлу,ПометкаУдаления"; 
			Если МетаданныеОбъектаФайла.Иерархический Тогда
				ИменаРеквизитов = ИменаРеквизитов + "," + "ЭтоГруппа";
			КонецЕсли;
			ФайлОбъект = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(ПрисоединенныйФайл, ИменаРеквизитов);
			Если Не МетаданныеОбъектаФайла.Иерархический Тогда
				ФайлОбъект.Вставить("ЭтоГруппа", Ложь);
			КонецЕсли;
		КонецЕсли;
	// Это справочник версий файлов.
	ИначеЕсли ОбщегоНазначения.ЕстьРеквизитОбъекта("РодительскаяВерсия", МетаданныеОбъектаФайла) Тогда
		ФайлОбъект = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(ПрисоединенныйФайл, 
			"Ссылка,ТипХраненияФайла,Наименование,Расширение,Том,ПутьКФайлу,Владелец,ПометкаУдаления");
		ФайлОбъект.Вставить("ВладелецФайла",
			ОбщегоНазначения.ЗначениеРеквизитаОбъекта(ФайлОбъект.Владелец, "ВладелецФайла"));
		ФайлОбъект.Удалить("Владелец");
		ФайлОбъект.Вставить("ЭтоГруппа", Ложь);
	КонецЕсли;
	
	Возврат ФайлОбъект;
	
КонецФункции

#КонецОбласти

#Область ОчисткаНенужныхФайлов

Функция ТекстЗапросаПолногоОбъемаФайлов() Экспорт
	МетаданныеСправочники = Метаданные.Справочники;
	ДобавитьПсевдонимПоля = Истина;
	ТекстЗапроса = "";
	Для Каждого Справочник Из МетаданныеСправочники Цикл
		Если Справочник.Реквизиты.Найти("ВладелецФайла") <> Неопределено Тогда
			
			ЕстьВозможностьХранитьВерсии = ОбщегоНазначения.ЕстьРеквизитОбъекта("ТекущаяВерсия", Справочник);
			Если ЕстьВозможностьХранитьВерсии Тогда
				СправочникВерсийФайлов =
					ОбщегоНазначения.ИдентификаторОбъектаМетаданных(Справочник.Реквизиты.ТекущаяВерсия.Тип.Типы()[0]);
				ПолноеИмяСправочникаВерсийФайлов = СправочникВерсийФайлов.ПолноеИмя;
				
				ЧастьТекстаЗапроса = "
					|ВЫБРАТЬ
					|	ТИПЗНАЧЕНИЯ(Файлы.ВладелецФайла) КАК ВладелецФайла,
					|	СУММА(ЕСТЬNULL(ВерсииФайлов.Размер, Файлы.Размер) / 1024 / 1024) КАК ОбъемВсего
					|ИЗ
					|	#ИмяСправочника КАК Файлы
					|		ЛЕВОЕ СОЕДИНЕНИЕ #ПолноеИмяСправочникаВерсийФайлов КАК ВерсииФайлов
					|		ПО Файлы.Ссылка = ВерсииФайлов.Владелец
					|ГДЕ
					|	НЕ Файлы.ПометкаУдаления
					|	И НЕ ЕСТЬNULL(ВерсииФайлов.ПометкаУдаления, ЛОЖЬ)
					|
					|СГРУППИРОВАТЬ ПО
					|	ТИПЗНАЧЕНИЯ(Файлы.ВладелецФайла)";
				
				ЧастьТекстаЗапроса = СтрЗаменить(ЧастьТекстаЗапроса, "#ИмяСправочника", "Справочник." + Справочник.Имя);
				ЧастьТекстаЗапроса = СтрЗаменить(ЧастьТекстаЗапроса, "#ПолноеИмяСправочникаВерсийФайлов",
					ПолноеИмяСправочникаВерсийФайлов);
				
				ТекстЗапроса = ТекстЗапроса + ?(ПустаяСтрока(ТекстЗапроса),"", " ОБЪЕДИНИТЬ ВСЕ ") + ЧастьТекстаЗапроса;
				
				Если ДобавитьПсевдонимПоля Тогда
					ДобавитьПсевдонимПоля = Ложь;
				КонецЕсли;
			Иначе
				ТекстЗапроса = ТекстЗапроса + ?(ПустаяСтрока(ТекстЗапроса),"", " ОБЪЕДИНИТЬ ВСЕ") + "
					|
					|ВЫБРАТЬ
					|	ТИПЗНАЧЕНИЯ(Файлы.ВладелецФайла) " + ?(ДобавитьПсевдонимПоля, "КАК ВладелецФайла,",",") + "
					|	Файлы.Размер / 1024 / 1024 " + ?(ДобавитьПсевдонимПоля, "КАК ОбъемВсего","") + "
					|ИЗ
					|	Справочник." + Справочник.Имя + " КАК Файлы
					|ГДЕ
					|	НЕ Файлы.ПометкаУдаления";
				
				Если ДобавитьПсевдонимПоля Тогда
					ДобавитьПсевдонимПоля = Ложь;
				КонецЕсли;
			КонецЕсли;
				
		КонецЕсли;
	КонецЦикла;
	
	Возврат ТекстЗапроса;
	
КонецФункции

#КонецОбласти

#Область РаботаСТомамиФайлов

// Возвращает тип хранения файлов с учетом наличия томов.
// Если томов хранения файлов нет, тогда хранение в ИБ.
//
// Возвращаемое значение:
//   ПеречислениеСсылка.ТипыХраненияФайлов
//
Функция ТипХраненияФайлов() Экспорт
	
	Если РаботаСФайламиВТомахСлужебный.ХранитьФайлыВТомахНаДиске()
		И РаботаСФайламиВТомахСлужебный.ЕстьТомаХраненияФайлов() Тогда
		Возврат Перечисления.ТипыХраненияФайлов.ВТомахНаДиске;
	Иначе
		Возврат Перечисления.ТипыХраненияФайлов.ВИнформационнойБазе;
	КонецЕсли;

КонецФункции

// Возвращает тип хранения файла с учетом наличия томов или смешанного хранения.
// Если томов хранения файлов нет, тогда хранение в ИБ.
//
// Параметры:
//  РазмерФайла - Число
//  РасширениеФайла - Строка 
//
// Возвращаемое значение:
//   ПеречислениеСсылка.ТипыХраненияФайлов
//
Функция ТипХраненияФайла(Знач РазмерФайла, Знач РасширениеФайла) Экспорт
	
	Если РаботаСФайламиВТомахСлужебный.ХранитьФайлыВТомахНаДиске()
		И РаботаСФайламиВТомахСлужебный.ЕстьТомаХраненияФайлов() Тогда
		Возврат РаботаСФайламиВТомахСлужебный.ТипХраненияФайла(РазмерФайла, РасширениеФайла);
	Иначе
		Возврат Перечисления.ТипыХраненияФайлов.ВИнформационнойБазе;
	КонецЕсли;

КонецФункции

// Проверяет, что хотя бы в одном томе есть хотя бы один файл.
//
// Возвращаемое значение:
//  Булево
//
Функция ЕстьФайлыВТомах() Экспорт
	
	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ ПЕРВЫЕ 1
	|	СведенияОФайлах.Файл КАК Файл
	|ИЗ
	|	РегистрСведений.СведенияОФайлах КАК СведенияОФайлах
	|ГДЕ
	|	СведенияОФайлах.ТипХраненияФайла = ЗНАЧЕНИЕ(Перечисление.ТипыХраненияФайлов.ВТомахНаДиске)";
	
	Возврат Не Запрос.Выполнить().Пустой();
	
КонецФункции

#КонецОбласти

#Область УправлениеДоступом

Функция ЭтоСправочникФайловИлиВерсийФайлов(ПолноеИмя) Экспорт
	
	ЧастиИмени = СтрРазделить(ПолноеИмя, ".", Ложь);
	Если ЧастиИмени.Количество() <> 2 Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Если ВРег(ЧастиИмени[0]) <> ВРег("Справочник")
	   И ВРег(ЧастиИмени[0]) <> ВРег("Catalog") Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Если СтрЗаканчиваетсяНа(ВРег(ЧастиИмени[1]), ВРег(РаботаСФайламиКлиентСервер.СуффиксСправочникаПрисоединенныеФайлы()))
	 Или ВРег(ЧастиИмени[1]) = ВРег("Файлы")
	 Или ВРег(ЧастиИмени[1]) = ВРег("ВерсииФайлов") Тогда
		
		Возврат Истина;
	КонецЕсли;
	
	Возврат Ложь;
	
КонецФункции

#КонецОбласти

#Область ЭлектроннаяПодписьИШифрование

Функция ДоступнаЭлектроннаяПодпись(ТипФайла) Экспорт
	
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ЭлектроннаяПодпись") Тогда
		МодульЭлектроннаяПодписьСлужебный = ОбщегоНазначения.ОбщийМодуль("ЭлектроннаяПодписьСлужебный");
		Возврат МодульЭлектроннаяПодписьСлужебный.ДоступнаЭлектроннаяПодпись(ТипФайла);
	КонецЕсли;
	
	Возврат Ложь;
	
КонецФункции

// Управляет видимостью элементов и команд в зависимости от наличия и
// использования электронной подписи и шифрования.
// 
// Параметры:
//   Форма - ФормаКлиентскогоПриложения
//   ЭтоФормаСписка - Булево
//   ТолькоКартинкаСтрок - Булево
//
Процедура КриптографияПриСозданииФормыНаСервере(Форма, ЭтоФормаСписка = Истина, ТолькоКартинкаСтрок = Ложь) Экспорт
	
	Элементы = Форма.Элементы;
	
	Подписание = Ложь;
	Шифрование = Ложь;
	ПросмотрЗашифрованных = Ложь;
	ДоступнаУсовершенствованнаяПодпись = Ложь;
	ВидимостьИнструкции = Ложь;
	
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ЭлектроннаяПодпись") Тогда
	
		МодульЭлектроннаяПодписьСлужебный = ОбщегоНазначения.ОбщийМодуль("ЭлектроннаяПодписьСлужебный");
		Если МодульЭлектроннаяПодписьСлужебный.ИнтерактивноеИспользованиеЭлектронныхПодписейИШифрования() Тогда
			МодульЭлектроннаяПодпись = ОбщегоНазначения.ОбщийМодуль("ЭлектроннаяПодпись");
			Подписание            = МодульЭлектроннаяПодпись.ДобавлениеИзменениеЭлектронныхПодписей();
			Шифрование            = МодульЭлектроннаяПодпись.ШифрованиеИРасшифровкаДанных();
			ПросмотрЗашифрованных = МодульЭлектроннаяПодпись.РасшифровкаДанных();
			ДоступнаУсовершенствованнаяПодпись = МодульЭлектроннаяПодпись.ДоступнаУсовершенствованнаяПодпись();
			ВидимостьИнструкции = МодульЭлектроннаяПодписьСлужебный.ВидимостьСсылкиНаИнструкциюПоТипичнымПроблемамПриРаботеСПрограммами();
		КонецЕсли;
		
	КонецЕсли;
	
	Если ЭтоФормаСписка Тогда
		Если ОбщегоНазначения.ЭтоСправочник(ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(Форма.Список.ОсновнаяТаблица)) Тогда
			ТаблицаФайлов = ОбщегоНазначения.МенеджерОбъектаПоПолномуИмени(Форма.Список.ОсновнаяТаблица);
			Доступна = ДоступнаЭлектроннаяПодпись(ТипЗнч(ТаблицаФайлов.ПустаяСсылка()));
		Иначе
			Доступна = Истина;
		КонецЕсли;
	Иначе
		Доступна = ДоступнаЭлектроннаяПодпись(ТипЗнч(Форма.Объект.Ссылка));
	КонецЕсли;
	
	Если Не ТолькоКартинкаСтрок Тогда
		
		Элементы.ФормаГруппаКомандЭлектроннаяПодписьИШифрование.Видимость = Доступна;
		
		Элементы.ФормаПодписать.Видимость = Подписание;
		Элементы.ФормаДобавитьПодписьИзФайла.Видимость = Подписание;
		Элементы.ФормаЗашифровать.Видимость = Шифрование;
		Элементы.ФормаРасшифровать.Видимость = ПросмотрЗашифрованных;
		
		Если ЭтоФормаСписка Тогда
			
			Элементы.СписокКонтекстноеМенюПодписать.Видимость = Подписание;
			Элементы.СписокКонтекстноеМенюДобавитьПодписьИзФайла.Видимость = Подписание;
			Элементы.СписокКонтекстноеМенюЗашифровать.Видимость = Шифрование;
			Элементы.СписокКонтекстноеМенюГруппаКомандЭлектроннаяПодписьИШифрование.Видимость = Доступна;
			
		Иначе
			
			Элементы.ЭлектронныеПодписиПродлитьДействиеПодписей.Видимость = Подписание И ДоступнаУсовершенствованнаяПодпись;
			Элементы.ЭлектронныеПодписи.ИзменятьСоставСтрок = Подписание;
			Элементы.ЭлектронныеПодписиПодписать.Видимость = Подписание;
			Элементы.ЭлектронныеПодписиУдалить.Видимость = Подписание;
			Элементы.ГруппаЭлектронныеПодписи.Видимость = Доступна;
			Элементы.ГруппаСертификатыШифрования.Видимость = ПросмотрЗашифрованных И Доступна;
			Элементы.Инструкция.Видимость = Подписание И ВидимостьИнструкции;
			
		КонецЕсли;
	КонецЕсли;
	
	Если Не Доступна Тогда
		Возврат;
	КонецЕсли;
	
	Если Не ТолькоКартинкаСтрок Тогда
		Элементы.ФормаГруппаКомандШифрование.Видимость = ПросмотрЗашифрованных;
		Если ЭтоФормаСписка Тогда
			Элементы.СписокКонтекстноеМенюГруппаКомандШифрование.Видимость = ПросмотрЗашифрованных;
		КонецЕсли;
	КонецЕсли;
	
	Если ПросмотрЗашифрованных Тогда
		Заголовок = НСтр("ru = 'Электронная подпись и шифрование'");
		Подсказка = НСтр("ru = 'Наличие электронной подписи или шифрования'");
		Картинка  = БиблиотекаКартинок["ПодписанЗашифрованЗаголовок"];
	Иначе
		Заголовок = НСтр("ru = 'Электронная подпись'");
		Подсказка = НСтр("ru = 'Наличие электронной подписи'");
		Картинка  = БиблиотекаКартинок["ПодписанЭП"];
	КонецЕсли;
	
	Если ЭтоФормаСписка Тогда
		Элементы.СписокНомерКартинкиПодписанЗашифрован.КартинкаШапки = Картинка;
		Элементы.СписокНомерКартинкиПодписанЗашифрован.Подсказка = Подсказка;
	КонецЕсли;
	
	Если Не ТолькоКартинкаСтрок Тогда
		
		ГруппаКомандЭП = Элементы.ФормаГруппаКомандЭлектроннаяПодписьИШифрование; //ГруппаФормы
		ГруппаКомандЭП.Заголовок = Заголовок;
		ГруппаКомандЭП.Подсказка = Заголовок;
		ГруппаКомандЭП.Картинка  = Картинка;
		
		Если ЭтоФормаСписка Тогда
			
			КомандаСпискаЭП = Элементы.СписокКонтекстноеМенюГруппаКомандЭлектроннаяПодписьИШифрование; // КнопкаФормы
			КомандаСпискаЭП.Заголовок = Заголовок;
			КомандаСпискаЭП.Подсказка = Заголовок;
			КомандаСпискаЭП.Картинка  = Картинка;
			
		КонецЕсли;
		
	КонецЕсли;
	
	МодульЭлектроннаяПодписьСлужебный = ОбщегоНазначения.ОбщийМодуль("ЭлектроннаяПодписьСлужебный");
	МодульЭлектроннаяПодписьСлужебный.ОформитьСписокПодписей(Форма, "ЭлектронныеПодписи");
	
КонецПроцедуры

// Только для внутреннего использования.
//
// Параметры:
//   ПодписиВФорме - ТаблицаЗначений
//
Процедура ПеренестиРезультатыПроверкиПодписей(ПодписиВФорме, ПодписанныйФайл) Экспорт
	
	Если Не ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ЭлектроннаяПодпись") Тогда
		Возврат;
	КонецЕсли;
		
	МодульЭлектроннаяПодписьСлужебный = ОбщегоНазначения.ОбщийМодуль("ЭлектроннаяПодписьСлужебный");
	Если Не МодульЭлектроннаяПодписьСлужебный.ДоступнаЭлектроннаяПодпись(ТипЗнч(ПодписанныйФайл)) Тогда
		Возврат;
	КонецЕсли;
		
	МодульЭлектроннаяПодпись = ОбщегоНазначения.ОбщийМодуль("ЭлектроннаяПодпись");
	ПодписиВОбъекте = МодульЭлектроннаяПодпись.УстановленныеПодписи(ПодписанныйФайл, Неопределено, Истина);
	
	Если ПодписиВФорме.Количество() <> ПодписиВОбъекте.Количество() Тогда
		Возврат; // Если объект был изменен, результаты проверки не переносятся.
	КонецЕсли;
	
	Если ПодписиВФорме.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	Свойства = Новый Структура("ДатаПроверкиПодписи, ПодписьВерна, ТребуетсяПроверка", Null, Null, Null);
	Свойства.Вставить("ТипПодписи", Null);
	Свойства.Вставить("СрокДействияПоследнейМеткиВремени", Null);
	Свойства.Вставить("РезультатПроверкиПодписиПоМЧД", Null);
	
	ЗаполнитьЗначенияСвойств(Свойства, ПодписиВФорме[0]);
	Если Свойства.ДатаПроверкиПодписи = Null
	 Или Свойства.ПодписьВерна = Null Тогда
		Возврат; // Если в форме нет реквизитов проверки, результаты проверки не переносятся.
	КонецЕсли;
	
	Если Свойства.ТребуетсяПроверка = Null Тогда
		Свойства.Удалить("ТребуетсяПроверка");
	КонецЕсли;
	Если Свойства.ТипПодписи = Null Тогда
		Свойства.Удалить("ТипПодписи");
	КонецЕсли;
	Если Свойства.СрокДействияПоследнейМеткиВремени = Null Тогда
		Свойства.Удалить("СрокДействияПоследнейМеткиВремени");
	КонецЕсли;
	Если Свойства.РезультатПроверкиПодписиПоМЧД = Null Тогда
		Свойства.Удалить("РезультатПроверкиПодписиПоМЧД");
	КонецЕсли;
	
	Для Каждого Строка Из ПодписиВФорме Цикл
		СтрокаВОбъекте = ПодписиВОбъекте.Получить(ПодписиВФорме.Индекс(Строка));
		Если Строка.ДатаПодписи         <> СтрокаВОбъекте.ДатаПодписи
		 Или Строка.Комментарий         <> СтрокаВОбъекте.Комментарий
		 Или Строка.КомуВыданСертификат <> СтрокаВОбъекте.КомуВыданСертификат И ЗначениеЗаполнено(СтрокаВОбъекте.КомуВыданСертификат)
		 Или Строка.Отпечаток           <> СтрокаВОбъекте.Отпечаток И ЗначениеЗаполнено(СтрокаВОбъекте.Отпечаток)
		 Или Строка.УстановившийПодпись <> СтрокаВОбъекте.УстановившийПодпись Тогда
			Возврат; // Если объект был изменен, результаты проверки не переносятся.
		КонецЕсли;
	КонецЦикла;
	
	Свойства.Вставить("Отпечаток");
	Свойства.Вставить("КомуВыданСертификат");
	
	Для Каждого Строка Из ПодписиВФорме Цикл
		СтрокаВОбъекте = ПодписиВОбъекте.Получить(ПодписиВФорме.Индекс(Строка));
		ЗаполнитьЗначенияСвойств(Свойства, СтрокаВОбъекте);
		ЕстьИзменения = Ложь;
		Для Каждого КлючИЗначение Из Свойства Цикл
			
			// Локализация
			Если КлючИЗначение.Ключ = "РезультатПроверкиПодписиПоМЧД" Тогда
				НовыйРезультат = Неопределено;
				Если ЗначениеЗаполнено(Строка[КлючИЗначение.Ключ]) Тогда
					НовыйРезультат = ПолучитьИзВременногоХранилища(Строка[КлючИЗначение.Ключ]);
				КонецЕсли;
				Если ТипЗнч(НовыйРезультат) <> ТипЗнч(Свойства[КлючИЗначение.Ключ]) Тогда
					ЕстьИзменения = Истина;
				ИначеЕсли ЗначениеЗаполнено(НовыйРезультат) И ЗначениеЗаполнено(Свойства[КлючИЗначение.Ключ])
					И Не ОбщегоНазначения.КоллекцииИдентичны(НовыйРезультат, Свойства[КлючИЗначение.Ключ],,
						"ДатаПроверки, ДополнительныеДанные") Тогда
					ЕстьИзменения = Истина;
				КонецЕсли;
				Продолжить;
			КонецЕсли;
			// Конец Локализация
			
			Если Строка[КлючИЗначение.Ключ] <> Свойства[КлючИЗначение.Ключ] Тогда
				ЕстьИзменения = Истина;
			КонецЕсли;
		КонецЦикла;
		
		Если Не ЕстьИзменения Тогда
			Продолжить; // Не нужно устанавливать модифицированность, если результаты проверки совпадают.
		КонецЕсли;
		ЗаполнитьЗначенияСвойств(Свойства, Строка);
		ЗаполнитьЗначенияСвойств(СтрокаВОбъекте, Свойства);
		МодульЭлектроннаяПодпись.ОбновитьПодпись(ПодписанныйФайл, СтрокаВОбъекте);
	КонецЦикла;
	
КонецПроцедуры

// Параметры записи информации о шифровании.
// 
// Возвращаемое значение:
//  Структура - параметры записи информации о шифровании:
//   * Зашифровать - Булево - Истина (по умолчанию) - зашифровать файл, Ложь - расшифровать.
//   * МассивДанныхДляЗанесенияВБазу - Массив из Структура
//   * УникальныйИдентификатор - УникальныйИдентификатор - уникальный идентификатор формы.
//   * ИмяРабочегоКаталога - Строка - рабочий каталог.
//   * МассивФайловВРабочемКаталогеДляУдаления - Массив - файлы, которые надо удалить из регистра.
//   * МассивОтпечатков - Массив - массив отпечатков сертификатов, использованных для шифрования.
//   * СведенияОФайле - см. РаботаСФайламиКлиентСервер.СведенияОФайле
//
Функция ПараметрыЗаписиИнформацииОШифровании() Экспорт
	
	Параметры = Новый Структура;
	Параметры.Вставить("Зашифровать", Истина);
	Параметры.Вставить("МассивДанныхДляЗанесенияВБазу", Новый Массив);
	Параметры.Вставить("УникальныйИдентификатор", Неопределено);
	Параметры.Вставить("ИмяРабочегоКаталога", "");
	Параметры.Вставить("МассивФайловВРабочемКаталогеДляУдаления", Новый Массив);
	Параметры.Вставить("МассивОтпечатков", Новый Массив);
	Параметры.Вставить("СведенияОФайле", Неопределено);
	
	Возврат Параметры;
	
КонецФункции

// Помещает шифрованные файлы в базу и ставит признак Зашифрован файлу и всем версиям.
//
// Параметры:
//  ФайлСсылка - СправочникСсылка.Файлы - файл.
//  ПараметрыЗаписиИнформацииОШифровании - см. ПараметрыЗаписиИнформацииОШифровании
//
Процедура ЗаписатьИнформациюОШифровании(ФайлСсылка, ПараметрыЗаписиИнформацииОШифровании) Экспорт
	
	МетаданныеОбъекта = ФайлСсылка.Метаданные();
	Если ОбщегоНазначения.ЕстьРеквизитОбъекта("ТекущаяВерсия", МетаданныеОбъекта) Тогда
		ТекущаяВерсияФайла = ТекущаяВерсия(ФайлСсылка);
	КонецЕсли;
	
	АдресВременногоХранилищаТекстаТекущейВерсии = "";
	АдресВременногоХранилищаОсновногоФайла      = "";
	
	НачатьТранзакцию();
	Попытка
		
		Для Каждого ДанныеДляЗаписиНаСервере Из ПараметрыЗаписиИнформацииОШифровании.МассивДанныхДляЗанесенияВБазу Цикл
			
			Если ТипЗнч(ДанныеДляЗаписиНаСервере.ВерсияСсылка) <> Тип("СправочникСсылка.ВерсииФайлов") Тогда
				АдресВременногоХранилищаОсновногоФайла = ДанныеДляЗаписиНаСервере.АдресВременногоХранилища;
				Продолжить;
			КонецЕсли;
			
			АдресВременногоХранилища = ДанныеДляЗаписиНаСервере.АдресВременногоХранилища;
			ВерсияСсылка = ДанныеДляЗаписиНаСервере.ВерсияСсылка; // СправочникСсылка.ВерсииФайлов
			АдресВременногоХранилищаТекста = ДанныеДляЗаписиНаСервере.АдресВременногоХранилищаТекста;
			
			Если ВерсияСсылка = ТекущаяВерсияФайла Тогда
				АдресВременногоХранилищаТекстаТекущейВерсии = АдресВременногоХранилищаТекста;
			КонецЕсли;
			
			ПолноеИмяФайлаВРабочемКаталоге = "";
			Если ЗначениеЗаполнено(ПараметрыЗаписиИнформацииОШифровании.ИмяРабочегоКаталога) Тогда
				ВРабочемКаталогеНаЧтение = Истина; // не используется 
				ВРабочемКаталогеВладельца = Истина;
				ПолноеИмяФайлаВРабочемКаталоге = РаботаСФайламиСлужебныйВызовСервера.ПолноеИмяФайлаВРабочемКаталоге(ВерсияСсылка, 
				ПараметрыЗаписиИнформацииОШифровании.ИмяРабочегоКаталога, ВРабочемКаталогеНаЧтение, ВРабочемКаталогеВладельца);
			КонецЕсли;
			
			Если Не ПустаяСтрока(ПолноеИмяФайлаВРабочемКаталоге) Тогда
				ПараметрыЗаписиИнформацииОШифровании.МассивФайловВРабочемКаталогеДляУдаления.Добавить(ПолноеИмяФайлаВРабочемКаталоге);
			КонецЕсли;
			
			РаботаСФайламиСлужебныйВызовСервера.УдалитьИзРегистра(ВерсияСсылка);
			
			Если ПараметрыЗаписиИнформацииОШифровании.СведенияОФайле = Неопределено Тогда
				
				РеквизитыВерсии = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(ВерсияСсылка, "Наименование, Комментарий,
					|Расширение, ДатаСоздания, ДатаМодификацииУниверсальная, Размер");
				
				СведенияОФайле = РаботаСФайламиКлиентСервер.СведенияОФайле("ФайлСВерсией");
				СведенияОФайле.ИмяБезРасширения = РеквизитыВерсии.Наименование;
				СведенияОФайле.Комментарий = РеквизитыВерсии.Комментарий;
				СведенияОФайле.АдресВременногоХранилищаФайла = АдресВременногоХранилища;
				СведенияОФайле.РасширениеБезТочки = РеквизитыВерсии.Расширение;
				СведенияОФайле.ВремяИзменения = РеквизитыВерсии.ДатаСоздания;
				СведенияОФайле.ВремяИзмененияУниверсальное = РеквизитыВерсии.ДатаМодификацииУниверсальная;
				СведенияОФайле.Размер = РеквизитыВерсии.Размер;
				СведенияОФайле.НовыйСтатусИзвлеченияТекста = Перечисления.СтатусыИзвлеченияТекстаФайлов.НеИзвлечен;
				СведенияОФайле.Зашифрован = ПараметрыЗаписиИнформацииОШифровании.Зашифровать;
				СведенияОФайле.ХранитьВерсии = Ложь;
			Иначе
				СведенияОФайле = ПараметрыЗаписиИнформацииОШифровании.СведенияОФайле;
			КонецЕсли;
			
			// @skip-check query-in-loop - По-объектная запись данных.
			ОбновитьВерсиюФайла(ФайлСсылка, СведенияОФайле, ВерсияСсылка, ПараметрыЗаписиИнформацииОШифровании.УникальныйИдентификатор);
			
			// Для варианта с хранением файлов в томах удаляем Файл из временного хранилища после получения.
			Если Не ПустаяСтрока(ДанныеДляЗаписиНаСервере.АдресФайла) И ЭтоАдресВременногоХранилища(ДанныеДляЗаписиНаСервере.АдресФайла) Тогда
				УдалитьИзВременногоХранилища(ДанныеДляЗаписиНаСервере.АдресФайла);
			КонецЕсли;
			
		КонецЦикла;
		
		БлокировкаДанных = Новый БлокировкаДанных;
		ЭлементБлокировкиДанных = БлокировкаДанных.Добавить(Метаданные.НайтиПоТипу(ТипЗнч(ФайлСсылка)).ПолноеИмя());
		ЭлементБлокировкиДанных.УстановитьЗначение("Ссылка", ФайлСсылка);
		БлокировкаДанных.Заблокировать();
		
		ФайлОбъект = ФайлСсылка.ПолучитьОбъект();
		ЗаблокироватьДанныеДляРедактирования(ФайлСсылка, , ПараметрыЗаписиИнформацииОШифровании.УникальныйИдентификатор);
		
		ФайлОбъект.Зашифрован = ПараметрыЗаписиИнформацииОШифровании.Зашифровать;
		ФайлОбъект.ТекстХранилище = Новый ХранилищеЗначения("");
		ФайлОбъект.СтатусИзвлеченияТекста = Перечисления.СтатусыИзвлеченияТекстаФайлов.НеИзвлечен;
		
		// Чтобы прошла запись ранее подписанного объекта.
		ФайлОбъект.ДополнительныеСвойства.Вставить("ЗаписьПодписанногоОбъекта", Истина);
		
		Если ПараметрыЗаписиИнформацииОШифровании.Зашифровать Тогда
			Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ЭлектроннаяПодпись") Тогда
				МодульЭлектроннаяПодписьСлужебный = ОбщегоНазначения.ОбщийМодуль("ЭлектроннаяПодписьСлужебный");
				МодульЭлектроннаяПодписьСлужебный.ДобавитьСертификатыШифрования(ФайлСсылка, ПараметрыЗаписиИнформацииОШифровании.МассивОтпечатков);
			КонецЕсли;
		Иначе
			Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ЭлектроннаяПодпись") Тогда
				МодульЭлектроннаяПодписьСлужебный = ОбщегоНазначения.ОбщийМодуль("ЭлектроннаяПодписьСлужебный");
				МодульЭлектроннаяПодписьСлужебный.ОчиститьСертификатыШифрования(ФайлСсылка);
			КонецЕсли;
		КонецЕсли;
		
		МетаданныеФайла = Метаданные.НайтиПоТипу(ТипЗнч(ФайлСсылка));
		ИспользованиеПолнотекстовогоПоиска = Метаданные.СвойстваОбъектов.ИспользованиеПолнотекстовогоПоиска.Использовать;
		
		Если Не ПараметрыЗаписиИнформацииОШифровании.Зашифровать И АдресВременногоХранилищаТекстаТекущейВерсии <> "" Тогда
			
			Если МетаданныеФайла.ПолнотекстовыйПоиск = ИспользованиеПолнотекстовогоПоиска Тогда
				РезультатИзвлеченияТекста = ИзвлечьТекст(АдресВременногоХранилищаТекстаТекущейВерсии);
				ФайлОбъект.СтатусИзвлеченияТекста = РезультатИзвлеченияТекста.СтатусИзвлеченияТекста;
				ФайлОбъект.ТекстХранилище = РезультатИзвлеченияТекста.ТекстХранилище;
			Иначе
				ФайлОбъект.СтатусИзвлеченияТекста = Перечисления.СтатусыИзвлеченияТекстаФайлов.НеИзвлечен;
				ФайлОбъект.ТекстХранилище = Новый ХранилищеЗначения("");
			КонецЕсли;
			
		КонецЕсли;
		
		МетаданныеФайла = Метаданные.НайтиПоТипу(ТипЗнч(ФайлСсылка));
		ВозможностьХранитьВерсии = ОбщегоНазначения.ЕстьРеквизитОбъекта("ТекущаяВерсия", МетаданныеФайла);
		Если Не ФайлОбъект.ХранитьВерсии Или (ВозможностьХранитьВерсии И Не ЗначениеЗаполнено(ТекущаяВерсияФайла)) Тогда
			ОбновитьДвоичныеДанныеФайлаНаСервере(ФайлОбъект, АдресВременногоХранилищаОсновногоФайла);
		КонецЕсли;
		
		ФайлОбъект.Записать();
		РазблокироватьДанныеДляРедактирования(ФайлСсылка, ПараметрыЗаписиИнформацииОШифровании.УникальныйИдентификатор);
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		РазблокироватьДанныеДляРедактирования(ФайлСсылка, ПараметрыЗаписиИнформацииОШифровании.УникальныйИдентификатор);
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

Процедура ПроверитьФайлОбработан(ФайлСсылка, ИмяПроцедуры) Экспорт
	
	ОбновлениеИнформационнойБазы.ПроверитьОбъектОбработан(ФайлСсылка,,
		"РаботаСФайлами.ПеренестиЭлектронныеПодписиИСертификатыШифрованияВРегистрыСведений",
		ИмяПроцедуры);
	
КонецПроцедуры


#КонецОбласти

#Область КонтрольВеденияУчета

// См. КонтрольВеденияУчетаПереопределяемый.ПриОпределенииПроверок
Процедура ПриОпределенииПроверок(ГруппыПроверок, Проверки) Экспорт
	
	РаботаСФайламиВТомахСлужебный.ПриОпределенииПроверок(ГруппыПроверок, Проверки);
	
КонецПроцедуры

#КонецОбласти

#Область ИзвлечениеТекстаДляПолнотекстовогоПоиска

Процедура ИзвлечьТекстИзФайлов() Экспорт
	
	УстановитьПривилегированныйРежим(Истина);
	
	Если Не ОбщегоНазначения.ЭтоWindowsСервер() Тогда
		Возврат; // Извлечение текста работает только под Windows.
	КонецЕсли;
	
	ЗаписьЖурналаРегистрации(НСтр("ru = 'Файлы.Извлечение текста'", ОбщегоНазначения.КодОсновногоЯзыка()),
		УровеньЖурналаРегистрации.Информация,,, НСтр("ru = 'Начато регламентное извлечения текста'"));
	
	Запрос = Новый Запрос(ТекстЗапросаДляИзвлеченияТекста());
	ФайлыДляИзвлеченияТекста = Запрос.Выполнить().Выгрузить();
	
	Для Каждого ФайлБезТекста Из ФайлыДляИзвлеченияТекста Цикл
		
		ФайлЗаблокирован = Ложь;
		
		Попытка
			ИмяФайлаСДвоичнымиДанными = ИзвлечьТекстИзФайла(ФайлБезТекста, ФайлЗаблокирован);
		Исключение
			Если ФайлЗаблокирован Тогда
				ПоляФайла = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(ФайлБезТекста.Ссылка,
					"Наименование, Расширение");
				ЗаписьЖурналаРегистрации(НСтр("ru = 'Файлы.Извлечение текста'", ОбщегоНазначения.КодОсновногоЯзыка()),
					УровеньЖурналаРегистрации.Ошибка,,,
					СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
						НСтр("ru = 'Не удалось выполнить регламентное извлечение текста из файла
						           |""%1""
						           |по причине:
						           |""%2"".'"),
						ОбщегоНазначенияКлиентСервер.ПолучитьИмяСРасширением(ПоляФайла.Наименование, ПоляФайла.Расширение),
						ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()) ));
			КонецЕсли;
		КонецПопытки;
		
		Если ЗначениеЗаполнено(ИмяФайлаСДвоичнымиДанными) Тогда
			Файл = Новый Файл(ИмяФайлаСДвоичнымиДанными);
			Если Файл.Существует() Тогда
				Попытка
					УдалитьФайлы(ИмяФайлаСДвоичнымиДанными);
				Исключение
					ЗаписьЖурналаРегистрации(НСтр("ru = 'Файлы.Извлечение текста'", ОбщегоНазначения.КодОсновногоЯзыка()),
						УровеньЖурналаРегистрации.Ошибка,,, ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
				КонецПопытки;
			КонецЕсли;
		КонецЕсли;
		
	КонецЦикла;
	
	ЗаписьЖурналаРегистрации(НСтр("ru = 'Файлы.Извлечение текста'", ОбщегоНазначения.КодОсновногоЯзыка()),
		УровеньЖурналаРегистрации.Информация,,, НСтр("ru = 'Закончено регламентное извлечение текста'"));
	
КонецПроцедуры

// Возвращает Истина, если текст из файлов извлекается на сервере, а не на клиенте.
//
// Возвращаемое значение:
//  Булево -  Ложь - если текст не извлекается на сервере,
//                 т.е. может и должен быть извлечен на клиенте.
//
Функция ИзвлекатьТекстыФайловНаСервере() Экспорт
	
	УстановитьПривилегированныйРежим(Истина);
	
	Возврат Константы.ИзвлекатьТекстыФайловНаСервере.Получить();
	
КонецФункции

// Записывает на сервер результат извлечения текста - извлеченный текст и СтатусИзвлеченияТекста.
Процедура ЗаписатьРезультатИзвлеченияТекста(ФайлИлиВерсияСсылка, РезультатИзвлечения,
				АдресВременногоХранилищаТекста) Экспорт
				
	МетаданныеФайла = ФайлИлиВерсияСсылка.Метаданные();
	ИспользованиеПолнотекстовогоПоиска = Метаданные.СвойстваОбъектов.ИспользованиеПолнотекстовогоПоиска.Использовать;
	
	БлокировкаДанных = Новый БлокировкаДанных;
	ЭлементБлокировкиДанных = БлокировкаДанных.Добавить(МетаданныеФайла.ПолноеИмя());
	ЭлементБлокировкиДанных.УстановитьЗначение("Ссылка", ФайлИлиВерсияСсылка);
	
	Если Не ОбщегоНазначения.ЕстьРеквизитОбъекта("ВладелецФайла", МетаданныеФайла) Тогда
		Владелец = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(ФайлИлиВерсияСсылка, "Владелец");
		ЭлементБлокировкиДанных = БлокировкаДанных.Добавить(Владелец.Метаданные().ПолноеИмя());
		ЭлементБлокировкиДанных.УстановитьЗначение("Ссылка", Владелец);
	КонецЕсли;
	
	НачатьТранзакцию();
	Попытка
		БлокировкаДанных.Заблокировать();
		ЗаблокироватьДанныеДляРедактирования(ФайлИлиВерсияСсылка);
		
		ФайлИлиВерсияОбъект = ФайлИлиВерсияСсылка.ПолучитьОбъект();
		Если ФайлИлиВерсияОбъект <> Неопределено Тогда
			
			Если Не ПустаяСтрока(АдресВременногоХранилищаТекста) Тогда
				Если МетаданныеФайла.ПолнотекстовыйПоиск = ИспользованиеПолнотекстовогоПоиска Тогда
					РезультатИзвлеченияТекста = ИзвлечьТекст(АдресВременногоХранилищаТекста);
					ФайлИлиВерсияОбъект.СтатусИзвлеченияТекста = РезультатИзвлеченияТекста.СтатусИзвлеченияТекста;
					ФайлИлиВерсияОбъект.ТекстХранилище = РезультатИзвлеченияТекста.ТекстХранилище;
				Иначе
					ФайлИлиВерсияОбъект.СтатусИзвлеченияТекста = Перечисления.СтатусыИзвлеченияТекстаФайлов.НеИзвлечен;
					ФайлИлиВерсияОбъект.ТекстХранилище = Новый ХранилищеЗначения("");
				КонецЕсли;
				УдалитьИзВременногоХранилища(АдресВременногоХранилищаТекста);
			КонецЕсли;
			
			Если РезультатИзвлечения = "НеИзвлечен" Тогда
				ФайлИлиВерсияОбъект.СтатусИзвлеченияТекста = Перечисления.СтатусыИзвлеченияТекстаФайлов.НеИзвлечен;
			ИначеЕсли РезультатИзвлечения = "Извлечен" Тогда
				ФайлИлиВерсияОбъект.СтатусИзвлеченияТекста = Перечисления.СтатусыИзвлеченияТекстаФайлов.Извлечен;
			ИначеЕсли РезультатИзвлечения = "ИзвлечьНеУдалось" Тогда
				ФайлИлиВерсияОбъект.СтатусИзвлеченияТекста = Перечисления.СтатусыИзвлеченияТекстаФайлов.ИзвлечьНеУдалось;
			КонецЕсли;
		
			ПриЗаписиИзвлеченногоТекста(ФайлИлиВерсияОбъект);
		КонецЕсли;
		
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

#КонецОбласти

#Область ПрочиеФункции

Функция СписокРасширенийДляПредпросмотра() Экспорт
	
	// См. также перечисление ФорматКартинки.
	РасширенияДляПредпросмотра = Новый СписокЗначений;
	РасширенияДляПредпросмотра.Добавить("bmp");
	РасширенияДляПредпросмотра.Добавить("emf");
	РасширенияДляПредпросмотра.Добавить("gif");
	РасширенияДляПредпросмотра.Добавить("ico");
	РасширенияДляПредпросмотра.Добавить("icon");
	РасширенияДляПредпросмотра.Добавить("jpg");
	РасширенияДляПредпросмотра.Добавить("jpeg");
	РасширенияДляПредпросмотра.Добавить("png");
	РасширенияДляПредпросмотра.Добавить("tiff");
	РасширенияДляПредпросмотра.Добавить("tif");
	РасширенияДляПредпросмотра.Добавить("wmf");
	
	Возврат РасширенияДляПредпросмотра;
	
КонецФункции

Функция СписокЗапрещенныхРасширений() Экспорт
	
	СписокЗапрещенныхРасширений = Новый СписокЗначений;
	СписокЗапрещенныхРасширений.Добавить("ade");
	СписокЗапрещенныхРасширений.Добавить("adp");
	СписокЗапрещенныхРасширений.Добавить("app");
	СписокЗапрещенныхРасширений.Добавить("bas");
	СписокЗапрещенныхРасширений.Добавить("bat");
	СписокЗапрещенныхРасширений.Добавить("chm");
	СписокЗапрещенныхРасширений.Добавить("class");
	СписокЗапрещенныхРасширений.Добавить("cmd");
	СписокЗапрещенныхРасширений.Добавить("com");
	СписокЗапрещенныхРасширений.Добавить("cpl");
	СписокЗапрещенныхРасширений.Добавить("crt");
	СписокЗапрещенныхРасширений.Добавить("dll");
	СписокЗапрещенныхРасширений.Добавить("exe");
	СписокЗапрещенныхРасширений.Добавить("fxp");
	СписокЗапрещенныхРасширений.Добавить("hlp");
	СписокЗапрещенныхРасширений.Добавить("hta");
	СписокЗапрещенныхРасширений.Добавить("ins");
	СписокЗапрещенныхРасширений.Добавить("isp");
	СписокЗапрещенныхРасширений.Добавить("jse");
	СписокЗапрещенныхРасширений.Добавить("js");
	СписокЗапрещенныхРасширений.Добавить("lnk");
	СписокЗапрещенныхРасширений.Добавить("mda");
	СписокЗапрещенныхРасширений.Добавить("mdb");
	СписокЗапрещенныхРасширений.Добавить("mde");
	СписокЗапрещенныхРасширений.Добавить("mdt");
	СписокЗапрещенныхРасширений.Добавить("mdw");
	СписокЗапрещенныхРасширений.Добавить("mdz");
	СписокЗапрещенныхРасширений.Добавить("msc");
	СписокЗапрещенныхРасширений.Добавить("msi");
	СписокЗапрещенныхРасширений.Добавить("msp");
	СписокЗапрещенныхРасширений.Добавить("mst");
	СписокЗапрещенныхРасширений.Добавить("ops");
	СписокЗапрещенныхРасширений.Добавить("pcd");
	СписокЗапрещенныхРасширений.Добавить("pif");
	СписокЗапрещенныхРасширений.Добавить("prf");
	СписокЗапрещенныхРасширений.Добавить("prg");
	СписокЗапрещенныхРасширений.Добавить("reg");
	СписокЗапрещенныхРасширений.Добавить("scf");
	СписокЗапрещенныхРасширений.Добавить("scr");
	СписокЗапрещенныхРасширений.Добавить("sct");
	СписокЗапрещенныхРасширений.Добавить("shb");
	СписокЗапрещенныхРасширений.Добавить("shs");
	СписокЗапрещенныхРасширений.Добавить("url");
	СписокЗапрещенныхРасширений.Добавить("vb");
	СписокЗапрещенныхРасширений.Добавить("vbe");
	СписокЗапрещенныхРасширений.Добавить("vbs");
	СписокЗапрещенныхРасширений.Добавить("wsc");
	СписокЗапрещенныхРасширений.Добавить("wsf");
	СписокЗапрещенныхРасширений.Добавить("wsh");
	
	Возврат СписокЗапрещенныхРасширений;
	
КонецФункции

Функция СписокРасширенийТекстовыхФайлов() Экспорт
	
	Возврат "TXT XML INI"; 
	
КонецФункции

Функция ПодготовитьСтруктуруПараметровОтправки() Экспорт
	
	Возврат Новый Структура("Получатель,Тема,Текст", Неопределено, "", "");
	
КонецФункции

Процедура РегламентнаяСинхронизацияФайловWebdav(Параметры = Неопределено, АдресРезультата = Неопределено) Экспорт
	
	ОбщегоНазначения.ПриНачалеВыполненияРегламентногоЗадания(Метаданные.РегламентныеЗадания.СинхронизацияФайлов);
	
	УстановитьПривилегированныйРежим(Истина);
	УдалитьНесинхронизируемыеФайлы();
	
	Результат = УчетныеЗаписиСинхронизации();
	Для каждого Выборка Из Результат Цикл
		
		Если ПустаяСтрока(Выборка.Сервис) Тогда
			Продолжить;
		КонецЕсли;
		
		// @skip-check query-in-loop - Порционная обработка большого объема данных.
		СинхронизироватьФайлыСОблачнымСервисом(Выборка.Ссылка);
		
	КонецЦикла;
	
КонецПроцедуры

// Заполняет список указанными типами файлов. Значение элемента списка
// используется в функции РаботаСФайламиСлужебныйКлиент.РасширенияПоТипуФайла
// для сопоставления возможных расширений известному типу файлов.
//
// Параметры:
//   Список - СписокЗначений - список, в который будут добавлены поддерживаемые
//          типы файлов.
//
Процедура ЗаполнитьСписокТипамиФайлов(Список) Экспорт
	
	Список.Добавить("Изображения", НСтр("ru = 'Изображения (JPG, JPEG, PNG ...)'"));
	Список.Добавить("ОфисныеДокументы", НСтр("ru = 'Офисные документы (DOC, DOCX, XLS ...)'"));
	
КонецПроцедуры

#КонецОбласти

#Область ОбменФайлами

// Подготовка параметров и предварительные проверки перед созданием файлового начального образа.
//
Функция ПодготовитьДанныеДляСозданияФайловогоНачальногоОбраза(СтруктураПараметров) Экспорт
	
	Результат = Новый Структура("ДанныеПодготовлены, ТребуетсяПодтверждение, ТекстВопроса", Истина, Ложь, "");
	
	ПолноеИмяФайловойБазыWindows 	= СтруктураПараметров.ПолноеИмяФайловойБазыWindows;
	ПолноеИмяФайловойБазыLinux 		= СтруктураПараметров.ПолноеИмяФайловойБазыLinux;
	ПутьКАрхивуСФайламиТомовWindows = СтруктураПараметров.ПутьКАрхивуСФайламиТомовWindows;
	ПутьКАрхивуСФайламиТомовLinux 	= СтруктураПараметров.ПутьКАрхивуСФайламиТомовLinux;
	
	ПутьКАрхивуСФайламиТомов = "";
	ПолноеИмяФайловойБазы = "";
	
	ЕстьФайлыВТомах = Ложь;
	
	Если РаботаСФайлами.ЕстьТомаХраненияФайлов() Тогда
		ЕстьФайлыВТомах = ЕстьФайлыВТомах();
	КонецЕсли;
	
	Если ОбщегоНазначения.ЭтоWindowsСервер() Тогда
		
		ПутьКАрхивуСФайламиТомов = ПутьКАрхивуСФайламиТомовWindows;
		ПолноеИмяФайловойБазы = ПолноеИмяФайловойБазыWindows;
		
		Если Не ОбщегоНазначения.ИнформационнаяБазаФайловая() Тогда
			Если ЕстьФайлыВТомах И Не ПустаяСтрока(ПутьКАрхивуСФайламиТомов) И (Лев(ПутьКАрхивуСФайламиТомов, 2) <> "\\"
				ИЛИ СтрНайти(ПутьКАрхивуСФайламиТомов, ":") <> 0) Тогда
				
				ОбщегоНазначения.СообщитьПользователю(
					НСтр("ru = 'Путь к архиву с файлами томов должен быть задан
					           |в формате UNC (\\servername\resource)'"),
					,
					"ПутьКАрхивуСФайламиТомовWindows");
				Результат.ДанныеПодготовлены = Ложь;
			КонецЕсли;
			Если Не ПустаяСтрока(ПолноеИмяФайловойБазы) И (Лев(ПолноеИмяФайловойБазы, 2) <> "\\" ИЛИ СтрНайти(ПолноеИмяФайловойБазы, ":") <> 0) Тогда
				ОбщегоНазначения.СообщитьПользователю(
					НСтр("ru = 'Путь к файловой информационной базе должен быть задан
					           |в формате UNC (\\servername\resource)'"),
					,
					"ПолноеИмяФайловойБазыWindows");
				Результат.ДанныеПодготовлены = Ложь;
			КонецЕсли;
		КонецЕсли;
	Иначе
		ПутьКАрхивуСФайламиТомов = ПутьКАрхивуСФайламиТомовLinux;
		ПолноеИмяФайловойБазы = ПолноеИмяФайловойБазыLinux;
	КонецЕсли;
	
	Если ПустаяСтрока(ПолноеИмяФайловойБазы) Тогда
		ОбщегоНазначения.СообщитьПользователю(
			НСтр("ru = 'Укажите полное имя файловой базы (файл 1cv8.1cd)'"),,
			"ПолноеИмяФайловойБазыWindows");
		Результат.ДанныеПодготовлены = Ложь;
	ИначеЕсли Результат.ДанныеПодготовлены Тогда
		ФайлБазы = Новый Файл(ПолноеИмяФайловойБазы);
		
		Если ЕстьФайлыВТомах Тогда
			Если ПустаяСтрока(ПутьКАрхивуСФайламиТомов) Тогда
				ОбщегоНазначения.СообщитьПользователю(
					НСтр("ru = 'Укажите полное имя архива с файлами томов (файл *.zip)'"),, 
					"ПутьКАрхивуСФайламиТомовWindows");
				Результат.ДанныеПодготовлены = Ложь;
			Иначе
				Файл = Новый Файл(ПутьКАрхивуСФайламиТомов);
				
				Если Файл.Существует() И ФайлБазы.Существует() Тогда
					Результат.ТекстВопроса = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = 'Файлы ""%1"" и ""%2"" уже существуют.
							           |Заменить существующие файлы?'"), ПутьКАрхивуСФайламиТомов, ПолноеИмяФайловойБазы);
					Результат.ТребуетсяПодтверждение = Истина;
				ИначеЕсли Файл.Существует() Тогда
					Результат.ТекстВопроса = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = 'Файл ""%1"" уже существует.
							           |Заменить существующий файл?'"), ПутьКАрхивуСФайламиТомов);
					Результат.ТребуетсяПодтверждение = Истина;
				КонецЕсли;
			КонецЕсли;
		КонецЕсли;
		
		Если Результат.ДанныеПодготовлены Тогда
			Если ФайлБазы.Существует() И НЕ Результат.ТребуетсяПодтверждение Тогда
				Результат.ТекстВопроса = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = 'Файл ""%1"" уже существует.
						           |Заменить существующий файл?'"), ПолноеИмяФайловойБазы);
				Результат.ТребуетсяПодтверждение = Истина;
			КонецЕсли;
			
			// Создать временный каталог.
			// АПК:441-выкл Очистка временных файлов выполняется в процедуре СоздатьФайловыйНачальныйОбразНаСервере
			ИмяКаталога = ПолучитьИмяВременногоФайла();
			СоздатьКаталог(ИмяКаталога);
			
			// Создать временный каталог для файлов.
			ИмяКаталогаФайлов = ПолучитьИмяВременногоФайла();
			СоздатьКаталог(ИмяКаталогаФайлов);
			// АПК:441-вкл
			
			// Для передачи пути каталога файлов в обработчик ПриОтправкеДанныхФайла.
			СохранитьНастройку("ОбменФайлами", "ВременныйКаталог", ИмяКаталогаФайлов);
			
			// Добавляем в параметры переменные, которые потребуются для создания начального образа.
			СтруктураПараметров.Вставить("ИмяКаталога", ИмяКаталога);
			СтруктураПараметров.Вставить("ИмяКаталогаФайлов", ИмяКаталогаФайлов);
			СтруктураПараметров.Вставить("ЕстьФайлыВТомах", ЕстьФайлыВТомах);
			СтруктураПараметров.Вставить("ПутьКАрхивуСФайламиТомов", ПутьКАрхивуСФайламиТомов);
			СтруктураПараметров.Вставить("ПолноеИмяФайловойБазы", ПолноеИмяФайловойБазы);
		КонецЕсли;
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции

// Процедура - Создать файловый начальный образ на сервере
//
// Параметры:
//  Параметры - Структура:
//   * ИмяКаталога - Строка
//   * Язык - Строка
//   * ПутьКАрхивуСФайламиТомов - Строка
//   * ЕстьФайлыВТомах- Булево
//   * ИмяКаталогаФайлов - Строка
//   * ПолноеИмяФайловойБазы  - Строка
//  АдресХранилища - УникальныйИдентификатор
//
Процедура СоздатьФайловыйНачальныйОбразНаСервере(Параметры, АдресХранилища) Экспорт
	
	Попытка
		
		СтрокаСоединения = "File=""" + Параметры.ИмяКаталога + """;"
						 + "Locale=""" + Параметры.Язык + """;";
		ПланыОбмена.СоздатьНачальныйОбраз(Параметры.Узел, СтрокаСоединения);  // Собственно создание начального образа.
		
		Если Параметры.ЕстьФайлыВТомах Тогда
			ZipФайл = Новый ЗаписьZipФайла;
			ZipФайл.Открыть(Параметры.ПутьКАрхивуСФайламиТомов);
			
			ВременныеФайлы = Новый Массив;
			ВременныеФайлы = НайтиФайлы(Параметры.ИмяКаталогаФайлов, ПолучитьМаскуВсеФайлы());
			
			Для Каждого ВременныйФайл Из ВременныеФайлы Цикл
				Если ВременныйФайл.ЭтоФайл() Тогда
					ПутьВременногоФайла = ВременныйФайл.ПолноеИмя;
					ZipФайл.Добавить(ПутьВременногоФайла);
				КонецЕсли;
			КонецЦикла;
			
			ZipФайл.Записать();
			
			УдалитьФайлы(Параметры.ИмяКаталогаФайлов); // Удаляем вместе с файлами внутри.
		КонецЕсли;
		
	Исключение
		
		УдалитьФайлы(Параметры.ИмяКаталога);
		ВызватьИсключение;
		
	КонецПопытки;
	
	ПутьВременногоФайлаБазы = Параметры.ИмяКаталога + "\1Cv8.1CD";
	ПереместитьФайл(ПутьВременногоФайлаБазы, Параметры.ПолноеИмяФайловойБазы);
	
	// очистка
	УдалитьФайлы(Параметры.ИмяКаталога);
	
КонецПроцедуры

// Подготовка параметров и предварительные проверки перед созданием серверного начального образа.
//
Функция ПодготовитьДанныеДляСозданияСерверногоНачальногоОбраза(СтруктураПараметров) Экспорт
	
	Результат = Новый Структура("ДанныеПодготовлены, ТребуетсяПодтверждение, ТекстВопроса", Истина, Ложь, "");
	
	ПутьКАрхивуСФайламиТомовWindows = СтруктураПараметров.ПутьКАрхивуСФайламиТомовWindows;
	ПутьКАрхивуСФайламиТомовLinux 	= СтруктураПараметров.ПутьКАрхивуСФайламиТомовLinux;
	ПутьКАрхивуСФайламиТомов        = "";
	
	ЕстьФайлыВТомах = Ложь;
	
	Если РаботаСФайлами.ЕстьТомаХраненияФайлов() Тогда
		ЕстьФайлыВТомах = ЕстьФайлыВТомах();
	КонецЕсли;
	
	Если ОбщегоНазначения.ЭтоWindowsСервер() Тогда
		
		ПутьКАрхивуСФайламиТомов = ПутьКАрхивуСФайламиТомовWindows;
		
		Если ЕстьФайлыВТомах Тогда
			Если Не ПустаяСтрока(ПутьКАрхивуСФайламиТомов)
			   И (Лев(ПутьКАрхивуСФайламиТомов, 2) <> "\\"
			 ИЛИ СтрНайти(ПутьКАрхивуСФайламиТомов, ":") <> 0) Тогда
				
				ОбщегоНазначения.СообщитьПользователю(
					НСтр("ru = 'Путь к архиву с файлами томов должен быть
					           |в формате UNC (\\servername\resource).'"),
					,
					"ПутьКАрхивуСФайламиТомовWindows");
				Результат.ДанныеПодготовлены = Ложь;
			КонецЕсли;
		КонецЕсли;
		
	Иначе
		ПутьКАрхивуСФайламиТомов = ПутьКАрхивуСФайламиТомовLinux;
	КонецЕсли;
	
	Если Результат.ДанныеПодготовлены Тогда
		Если ЕстьФайлыВТомах И ПустаяСтрока(ПутьКАрхивуСФайламиТомов) Тогда
				ОбщегоНазначения.СообщитьПользователю(
					НСтр("ru = 'Укажите полное имя архива с файлами томов (файл *.zip)'"),
					,
					"ПутьКАрхивуСФайламиТомовWindows");
				Результат.ДанныеПодготовлены = Ложь;
		Иначе
			Если ЕстьФайлыВТомах Тогда
				Файл = Новый Файл(ПутьКАрхивуСФайламиТомов);
				Если Файл.Существует() Тогда
					Результат.ТекстВопроса = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = 'Файл ""%1"" уже существует.
							           |Заменить существующий файл?'"), ПутьКАрхивуСФайламиТомов);
					Результат.ТребуетсяПодтверждение = Истина;
				КонецЕсли;
			КонецЕсли;

			// АПК:441-выкл Очистка временных файлов выполняется в процедуре СоздатьФайловыйНачальныйОбразНаСервере			
			// Создать временный каталог.
			ИмяКаталога = ПолучитьИмяВременногоФайла();
			СоздатьКаталог(ИмяКаталога);
			
			// Создать временный каталог для файлов.
			ИмяКаталогаФайлов = ПолучитьИмяВременногоФайла();
			СоздатьКаталог(ИмяКаталогаФайлов);
			// АПК:441-вкл
			
			// Для передачи пути каталога файлов в обработчик ПриОтправкеДанныхФайла.
			СохранитьНастройку("ОбменФайлами", "ВременныйКаталог", ИмяКаталогаФайлов);
			
			// Добавляем в параметры переменные, которые потребуются для создания начального образа.
			СтруктураПараметров.Вставить("ЕстьФайлыВТомах", ЕстьФайлыВТомах);
			СтруктураПараметров.Вставить("ПутьФайла", ПутьКАрхивуСФайламиТомов);
			СтруктураПараметров.Вставить("ИмяКаталога", ИмяКаталога);
			СтруктураПараметров.Вставить("ИмяКаталогаФайлов", ИмяКаталогаФайлов);
		КонецЕсли;
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции

// Создать серверный начальный образ на сервере.
//
Процедура СоздатьСерверныйНачальныйОбразНаСервере(Параметры, АдресРезультата) Экспорт
	
	Попытка
		
		ПланыОбмена.СоздатьНачальныйОбраз(Параметры.Узел, Параметры.СтрокаСоединения);
		
		Если Параметры.ЕстьФайлыВТомах Тогда
			ZipФайл = Новый ЗаписьZipФайла;
			ПутьZIP = Параметры.ПутьФайла;
			ZipФайл.Открыть(ПутьZIP);
			
			ВременныеФайлы = Новый Массив;
			ВременныеФайлы = НайтиФайлы(Параметры.ИмяКаталогаФайлов, ПолучитьМаскуВсеФайлы());
			
			Для Каждого ВременныйФайл Из ВременныеФайлы Цикл
				Если ВременныйФайл.ЭтоФайл() Тогда
					ПутьВременногоФайла = ВременныйФайл.ПолноеИмя;
					ZipФайл.Добавить(ПутьВременногоФайла);
				КонецЕсли;
			КонецЦикла;
			
			ZipФайл.Записать();
			УдалитьФайлы(Параметры.ИмяКаталогаФайлов); // Удаляем вместе с файлами внутри.
		КонецЕсли;
		
	Исключение
		
		УдалитьФайлы(Параметры.ИмяКаталога);
		ВызватьИсключение;
		
	КонецПопытки;
	
	// очистка
	УдалитьФайлы(Параметры.ИмяКаталога);
	
КонецПроцедуры

#КонецОбласти

#Область ОбработчикиРегламентныхЗаданий

// Обработчик регламентного задания ИзвлечениеТекста.
// Извлекает текст из файлов для полнотекстового поиска.
//
Процедура ИзвлечьТекстИзФайловНаСервере() Экспорт
	
	ОбщегоНазначения.ПриНачалеВыполненияРегламентногоЗадания(Метаданные.РегламентныеЗадания.ИзвлечениеТекста);
	
	ИзвлечьТекстИзФайлов();
	
КонецПроцедуры

#КонецОбласти

#Область ИзвлечениеТекста

// Возвращает текст запроса для получения файлов у которых не извлечен текст.
//
// Параметры:
//  ПолучитьВсеФайлы - Булево - начальное значение Ложь. Позволяет отключить выборку
//                     файлов по частям, если передать Истина.
//
// Возвращаемое значение:
//  Строка - текст запроса.
//
Функция ТекстЗапросаДляИзвлеченияТекста(ПолучитьВсеФайлы = Ложь, ДополнительныеПоля = Ложь) Экспорт
	
	// Текст запроса формируется по всем справочникам присоединенных файлов.
	ТекстЗапроса = "";
	
	ТипыФайлов = Метаданные.ОпределяемыеТипы.ПрисоединенныйФайл.Тип.Типы();
	
	ВсеИменаСправочников = Новый Массив;
	
	Для Каждого Тип Из ТипыФайлов Цикл
		МетаданныеСправочникаФайлов = Метаданные.НайтиПоТипу(Тип);
		НеИспользоватьПолнотекстовыйПоиск = Метаданные.СвойстваОбъектов.ИспользованиеПолнотекстовогоПоиска.НеИспользовать;
		Если МетаданныеСправочникаФайлов.ПолнотекстовыйПоиск = НеИспользоватьПолнотекстовыйПоиск Тогда
			Продолжить;
		КонецЕсли;
		ВсеИменаСправочников.Добавить(МетаданныеСправочникаФайлов.Имя);
	КонецЦикла;
	
	ЧислоФайловВВыборке = Цел(100 / ВсеИменаСправочников.Количество());
	ЧислоФайловВВыборке = ?(ЧислоФайловВВыборке < 10, 10, ЧислоФайловВВыборке);
	
	Для каждого ИмяСправочника Из ВсеИменаСправочников Цикл
	
		Если НЕ ПустаяСтрока(ТекстЗапроса) Тогда
			ТекстЗапроса = ТекстЗапроса + "
				|
				|ОБЪЕДИНИТЬ ВСЕ
				|
				|";
		КонецЕсли;
		
		ТекстЗапроса = ТекстЗапроса + ТекстЗапросаДляФайловСНеизвлеченнымТекстом(ИмяСправочника,
			ЧислоФайловВВыборке, ПолучитьВсеФайлы, ДополнительныеПоля);
		КонецЦикла;
		
	Возврат ТекстЗапроса;
	
КонецФункции

#КонецОбласти

#Область Сканирование

Функция ПараметрыСканераВПеречисления(РазрешениеЧисло, ЦветностьЧисло, ПоворотЧисло, РазмерБумагиЧисло) Экспорт 
	
	Если РазрешениеЧисло = 200 Тогда
		Разрешение = Перечисления.РазрешенияСканированногоИзображения.dpi200;
	ИначеЕсли РазрешениеЧисло = 300 Тогда
		Разрешение = Перечисления.РазрешенияСканированногоИзображения.dpi300;
	ИначеЕсли РазрешениеЧисло = 600 Тогда
		Разрешение = Перечисления.РазрешенияСканированногоИзображения.dpi600;
	ИначеЕсли РазрешениеЧисло = 1200 Тогда
		Разрешение = Перечисления.РазрешенияСканированногоИзображения.dpi1200;
	КонецЕсли;
	
	Если ЦветностьЧисло = 0 Тогда
		Цветность = Перечисления.ЦветностиИзображения.Монохромное;
	ИначеЕсли ЦветностьЧисло = 1 Тогда
		Цветность = Перечисления.ЦветностиИзображения.ГрадацииСерого;
	ИначеЕсли ЦветностьЧисло = 2 Тогда
		Цветность = Перечисления.ЦветностиИзображения.Цветное;
	КонецЕсли;
	
	Если ПоворотЧисло = 0 Тогда
		Поворот = Перечисления.СпособыПоворотаИзображения.НетПоворота;
	ИначеЕсли ПоворотЧисло = 90 Тогда
		Поворот = Перечисления.СпособыПоворотаИзображения.ВправоНа90;
	ИначеЕсли ПоворотЧисло = 180 Тогда
		Поворот = Перечисления.СпособыПоворотаИзображения.ВправоНа180;
	ИначеЕсли ПоворотЧисло = 270 Тогда
		Поворот = Перечисления.СпособыПоворотаИзображения.ВлевоНа90;
	КонецЕсли;
	
	Если РазмерБумагиЧисло = 0 Тогда
		РазмерБумаги = Перечисления.РазмерыБумаги.НеЗадано;
	ИначеЕсли РазмерБумагиЧисло = 11 Тогда
		РазмерБумаги = Перечисления.РазмерыБумаги.A3;
	ИначеЕсли РазмерБумагиЧисло = 1 Тогда
		РазмерБумаги = Перечисления.РазмерыБумаги.A4;
	ИначеЕсли РазмерБумагиЧисло = 5 Тогда
		РазмерБумаги = Перечисления.РазмерыБумаги.A5;
	ИначеЕсли РазмерБумагиЧисло = 6 Тогда
		РазмерБумаги = Перечисления.РазмерыБумаги.B4;
	ИначеЕсли РазмерБумагиЧисло = 2 Тогда
		РазмерБумаги = Перечисления.РазмерыБумаги.B5;
	ИначеЕсли РазмерБумагиЧисло = 7 Тогда
		РазмерБумаги = Перечисления.РазмерыБумаги.B6;
	ИначеЕсли РазмерБумагиЧисло = 14 Тогда
		РазмерБумаги = Перечисления.РазмерыБумаги.C4;
	ИначеЕсли РазмерБумагиЧисло = 15 Тогда
		РазмерБумаги = Перечисления.РазмерыБумаги.C5;
	ИначеЕсли РазмерБумагиЧисло = 16 Тогда
		РазмерБумаги = Перечисления.РазмерыБумаги.C6;
	ИначеЕсли РазмерБумагиЧисло = 3 Тогда
		РазмерБумаги = Перечисления.РазмерыБумаги.USLetter;
	ИначеЕсли РазмерБумагиЧисло = 4 Тогда
		РазмерБумаги = Перечисления.РазмерыБумаги.USLegal;
	ИначеЕсли РазмерБумагиЧисло = 10 Тогда
		РазмерБумаги = Перечисления.РазмерыБумаги.USExecutive;
	КонецЕсли;
	
	Результат = Новый Структура;
	Результат.Вставить("Разрешение", Разрешение);
	Результат.Вставить("Цветность", Цветность);
	Результат.Вставить("Поворот", Поворот);
	Результат.Вставить("РазмерБумаги", РазмерБумаги);
	Возврат Результат;
	
КонецФункции

Функция ПреобразоватьФорматСканированияВФорматХранения(ФорматСканирования, СохранятьВPDF) Экспорт
	
	Если СохранятьВPDF Тогда
		Возврат Перечисления.ФорматыХраненияОдностраничныхФайлов.PDF;
	ИначеЕсли ФорматСканирования = Перечисления.ФорматыСканированногоИзображения.BMP Тогда
		Возврат Перечисления.ФорматыХраненияОдностраничныхФайлов.BMP;
	ИначеЕсли ФорматСканирования = Перечисления.ФорматыСканированногоИзображения.GIF Тогда
		Возврат Перечисления.ФорматыХраненияОдностраничныхФайлов.GIF;
	ИначеЕсли ФорматСканирования = Перечисления.ФорматыСканированногоИзображения.JPG Тогда
		Возврат Перечисления.ФорматыХраненияОдностраничныхФайлов.JPG;
	ИначеЕсли ФорматСканирования = Перечисления.ФорматыСканированногоИзображения.TIF Тогда
		Возврат Перечисления.ФорматыХраненияОдностраничныхФайлов.TIF;
	КонецЕсли;
	
	Возврат Перечисления.ФорматыХраненияОдностраничныхФайлов.PNG; 
	
КонецФункции

Функция КомандаСканироватьЛист() Экспорт
	Возврат "e1cib/command/Обработка.Сканирование.Команда.СканироватьЛист";
КонецФункции

Функция КонвертироватьНастройкиСканирования(Знач Настройки) Экспорт

	Настройки.Разрешение = -1;
	Если Настройки.Разрешение = Перечисления.РазрешенияСканированногоИзображения.dpi200 Тогда
		Настройки.Разрешение = 200;
	ИначеЕсли Настройки.Разрешение = Перечисления.РазрешенияСканированногоИзображения.dpi300 Тогда
		Настройки.Разрешение = 300;
	ИначеЕсли Настройки.Разрешение = Перечисления.РазрешенияСканированногоИзображения.dpi600 Тогда
		Настройки.Разрешение = 600;
	ИначеЕсли Настройки.Разрешение = Перечисления.РазрешенияСканированногоИзображения.dpi1200 Тогда
		Настройки.Разрешение = 1200;
	КонецЕсли;

	Настройки.Цветность = -1;
	Если Настройки.Цветность = Перечисления.ЦветностиИзображения.Монохромное Тогда
		Настройки.Цветность = 0;
	ИначеЕсли Настройки.Цветность = Перечисления.ЦветностиИзображения.ГрадацииСерого Тогда
		Настройки.Цветность = 1;
	ИначеЕсли Настройки.Цветность = Перечисления.ЦветностиИзображения.Цветное Тогда
		Настройки.Цветность = 2;
	КонецЕсли;

	Настройки.Поворот = 0;
	Если Настройки.Поворот = Перечисления.СпособыПоворотаИзображения.ВправоНа90 Тогда
		Настройки.Поворот = 90;
	ИначеЕсли Настройки.Поворот = Перечисления.СпособыПоворотаИзображения.ВправоНа180 Тогда
		Настройки.Поворот = 180;
	ИначеЕсли Настройки.Поворот = Перечисления.СпособыПоворотаИзображения.ВлевоНа90 Тогда
		Настройки.Поворот = 270;
	КонецЕсли;

	Настройки.РазмерБумаги = 0;
	Если Настройки.РазмерБумаги = Перечисления.РазмерыБумаги.НеЗадано Тогда
		Настройки.РазмерБумаги = 0;
	ИначеЕсли Настройки.РазмерБумаги = Перечисления.РазмерыБумаги.A3 Тогда
		Настройки.РазмерБумаги = 11;
	ИначеЕсли Настройки.РазмерБумаги = Перечисления.РазмерыБумаги.A4 Тогда
		Настройки.РазмерБумаги = 1;
	ИначеЕсли Настройки.РазмерБумаги = Перечисления.РазмерыБумаги.A5 Тогда
		Настройки.РазмерБумаги = 5;
	ИначеЕсли Настройки.РазмерБумаги = Перечисления.РазмерыБумаги.B4 Тогда
		Настройки.РазмерБумаги = 6;
	ИначеЕсли Настройки.РазмерБумаги = Перечисления.РазмерыБумаги.B5 Тогда
		Настройки.РазмерБумаги = 2;
	ИначеЕсли Настройки.РазмерБумаги = Перечисления.РазмерыБумаги.B6 Тогда
		Настройки.РазмерБумаги = 7;
	ИначеЕсли Настройки.РазмерБумаги = Перечисления.РазмерыБумаги.C4 Тогда
		Настройки.РазмерБумаги = 14;
	ИначеЕсли Настройки.РазмерБумаги = Перечисления.РазмерыБумаги.C5 Тогда
		Настройки.РазмерБумаги = 15;
	ИначеЕсли Настройки.РазмерБумаги = Перечисления.РазмерыБумаги.C6 Тогда
		Настройки.РазмерБумаги = 16;
	ИначеЕсли Настройки.РазмерБумаги = Перечисления.РазмерыБумаги.USLetter Тогда
		Настройки.РазмерБумаги = 3;
	ИначеЕсли Настройки.РазмерБумаги = Перечисления.РазмерыБумаги.USLegal Тогда
		Настройки.РазмерБумаги = 4;
	ИначеЕсли Настройки.РазмерБумаги = Перечисления.РазмерыБумаги.USExecutive Тогда
		Настройки.РазмерБумаги = 10;
	КонецЕсли;

	Настройки.СжатиеTIFF = 6; // БезСжатия
	Если Настройки.СжатиеTIFF = Перечисления.ВариантыСжатияTIFF.LZW Тогда
		Настройки.СжатиеTIFF = 2;
	ИначеЕсли Настройки.СжатиеTIFF = Перечисления.ВариантыСжатияTIFF.RLE Тогда
		Настройки.СжатиеTIFF = 5;
	ИначеЕсли Настройки.СжатиеTIFF = Перечисления.ВариантыСжатияTIFF.CCITT3 Тогда
		Настройки.СжатиеTIFF = 3;
	ИначеЕсли Настройки.СжатиеTIFF = Перечисления.ВариантыСжатияTIFF.CCITT4 Тогда
		Настройки.СжатиеTIFF = 4;
	КонецЕсли;

	Возврат Настройки;

КонецФункции

#КонецОбласти

#Область ОчисткаНенужныхФайлов

Процедура ОчиститьНенужныеФайлы(Параметры = Неопределено, АдресРезультата = Неопределено) Экспорт
	
	ОбщегоНазначения.ПриНачалеВыполненияРегламентногоЗадания(Метаданные.РегламентныеЗадания.ОчисткаНенужныхФайлов);
	
	УстановитьПривилегированныйРежим(Истина);
	
	Если Параметры <> Неопределено И ТипЗнч(Параметры) = Тип("Структура") И Параметры.Свойство("РучнойЗапуск") Тогда
		ОчищатьНенужныеФайлы = Перечисления.РежимыОчисткиФайлов.ОчищатьУдаленныеИНенужные;
	Иначе
		ОчищатьНенужныеФайлы = РежимОчисткиФайлов();
	КонецЕсли;
	ЗаписатьВЖурналСобытийОчисткиФайлов(СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		НСтр("ru = 'Начата регламентная очистка ненужных файлов (%1).'"), ОчищатьНенужныеФайлы));
	
	Если ОчищатьНенужныеФайлы = Перечисления.РежимыОчисткиФайлов.ОчищатьУдаленныеИНенужные Тогда
		НастройкиОчистки = РегистрыСведений.НастройкиОчисткиФайлов.ТекущиеНастройкиОчистки();
		НастройкиОчисткиФайлов = НастройкиОчистки.НайтиСтроки(Новый Структура("ЭтоНастройкаДляЭлементаСправочника", Ложь));
		Для Каждого Настройка Из НастройкиОчисткиФайлов Цикл
			
			МассивИсключений = Новый Массив;
			ДетализированныеНастройки = НастройкиОчистки.НайтиСтроки(Новый Структура(
				"ИдентификаторВладельца, ЭтоНастройкаДляЭлементаСправочника",
				Настройка.ВладелецФайла, Истина));
				
			Для Каждого ЭлементИсключение Из ДетализированныеНастройки Цикл
				ОчиститьФайлыПоНастройке(ЭлементИсключение, МассивИсключений);
			КонецЦикла;
			
			ОчиститьФайлыПоНастройке(Настройка, МассивИсключений);
		КонецЦикла;
	КонецЕсли;
	
	РаботаСФайламиВТомахСлужебный.ОчиститьУдаленныеФайлы();
	ЗаписатьВЖурналСобытийОчисткиФайлов(НСтр("ru = 'Завершена регламентная очистка ненужных файлов.'"));
	
КонецПроцедуры

Функция ОбъектыИсключенияПриОчисткеФайлов() Экспорт
	
	Возврат НастройкиФайлов().НеОчищатьФайлы;
	
КонецФункции

#КонецОбласти

#Область ДляВызоваИзДругихПодсистем

// См. СтандартныеПодсистемыСервер.ПриОтправкеДанныхПодчиненному.
Процедура ПриОтправкеДанныхПодчиненному(ЭлементДанных, ОтправкаЭлемента, СозданиеНачальногоОбраза, Получатель) Экспорт
	
	ПриОтправкеФайла(ЭлементДанных, ОтправкаЭлемента, СозданиеНачальногоОбраза, Получатель);
	
КонецПроцедуры

// См. СтандартныеПодсистемыСервер.ПриОтправкеДанныхГлавному.
Процедура ПриОтправкеДанныхГлавному(ЭлементДанных, ОтправкаЭлемента, Получатель) Экспорт
	
	ПриОтправкеФайла(ЭлементДанных, ОтправкаЭлемента);
	
КонецПроцедуры

// См. СтандартныеПодсистемыСервер.ПриПолученииДанныхОтПодчиненного.
Процедура ПриПолученииДанныхОтПодчиненного(ЭлементДанных, ПолучениеЭлемента, ОтправкаНазад, Отправитель) Экспорт
	
	ПриПолученииФайла(ЭлементДанных, ПолучениеЭлемента, Отправитель);
	
КонецПроцедуры

// См. СтандартныеПодсистемыСервер.ПриПолученииДанныхОтГлавного.
Процедура ПриПолученииДанныхОтГлавного(ЭлементДанных, ПолучениеЭлемента, ОтправкаНазад, Отправитель) Экспорт
	
	ПриПолученииФайла(ЭлементДанных, ПолучениеЭлемента);
	
КонецПроцедуры

// См. ОбщегоНазначенияПереопределяемый.ПриДобавленииИсключенийПоискаСсылок.
Процедура ПриДобавленииИсключенийПоискаСсылок(ИсключенияПоискаСсылок) Экспорт
	
	ИсключенияПоискаСсылок.Добавить(Метаданные.РегистрыСведений.ФайлыВРабочемКаталоге.ПолноеИмя());
	ИсключенияПоискаСсылок.Добавить(Метаданные.РегистрыСведений.СведенияОФайлах.ПолноеИмя());
	ИсключенияПоискаСсылок.Добавить("Справочник.Файлы.Реквизиты.ТекущаяВерсия");
	ИсключенияПоискаСсылок.Добавить("Справочник.ВерсииФайлов.Реквизиты.РодительскаяВерсия");
	
КонецПроцедуры

// Параметры:
//   ТекущиеДела - см. ТекущиеДелаСервер.ТекущиеДела.
//
Процедура ПриЗаполненииСпискаТекущихДел(ТекущиеДела) Экспорт
	
	МодульТекущиеДелаСервер = ОбщегоНазначения.ОбщийМодуль("ТекущиеДелаСервер");
	Если Не ПравоДоступа("Чтение", Метаданные.Справочники.Файлы)
		Или МодульТекущиеДелаСервер.ДелоОтключено("РедактируемыеФайлы") Тогда
		Возврат;
	КонецЕсли;
	
	КоличествоЗанятыхФайлов = КоличествоЗанятыхФайлов();
	
	// Процедура вызывается только при наличии подсистемы "Текущие дела", поэтому здесь
	// не делается проверка существования подсистемы.
	Разделы = МодульТекущиеДелаСервер.РазделыДляОбъекта(Метаданные.Справочники.Файлы.ПолноеИмя());
	
	Для Каждого Раздел Из Разделы Цикл
		
		ИдентификаторРедактируемыеФайлы = "РедактируемыеФайлы" + СтрЗаменить(Раздел.ПолноеИмя(), ".", "");
		Дело = ТекущиеДела.Добавить();
		Дело.Идентификатор  = ИдентификаторРедактируемыеФайлы;
		Дело.ЕстьДела       = КоличествоЗанятыхФайлов > 0;
		Дело.Представление  = НСтр("ru = 'Редактируемые файлы'");
		Дело.Количество     = КоличествоЗанятыхФайлов;
		Дело.Важное         = Ложь;
		Дело.Форма          = "Обработка.РаботаСФайлами.Форма.РедактируемыеФайлы";
		Дело.Владелец       = Раздел;
		
	КонецЦикла;
	
КонецПроцедуры

// См. ГрупповоеИзменениеОбъектовПереопределяемый.ПриОпределенииОбъектовСРедактируемымиРеквизитами.
Процедура ПриОпределенииОбъектовСРедактируемымиРеквизитами(Объекты) Экспорт
	Объекты.Вставить(Метаданные.Справочники.ПапкиФайлов.ПолноеИмя(), "РеквизитыРедактируемыеВГрупповойОбработке");
	Объекты.Вставить(Метаданные.Справочники.Файлы.ПолноеИмя(), "РеквизитыРедактируемыеВГрупповойОбработке");
	Объекты.Вставить(Метаданные.Справочники.ВерсииФайлов.ПолноеИмя(), "РеквизитыРедактируемыеВГрупповойОбработке");
	Объекты.Вставить(Метаданные.Справочники.ТомаХраненияФайлов.ПолноеИмя(), "РеквизитыРедактируемыеВГрупповойОбработке");
КонецПроцедуры

// См. ЗагрузкаДанныхИзФайлаПереопределяемый.ПриОпределенииСправочниковДляЗагрузкиДанных.
Процедура ПриОпределенииСправочниковДляЗагрузкиДанных(ЗагружаемыеСправочники) Экспорт
	
	// Синхронизация файлов с облачным сервисом.
	
	// Загрузка в справочник ТомаХраненияФайлов запрещена.
	СтрокаТаблицы = ЗагружаемыеСправочники.Найти(Метаданные.Справочники.ТомаХраненияФайлов.ПолноеИмя(), "ПолноеИмя");
	Если СтрокаТаблицы <> Неопределено Тогда 
		ЗагружаемыеСправочники.Удалить(СтрокаТаблицы);
	КонецЕсли;
	
КонецПроцедуры

// См. РегламентныеЗаданияПереопределяемый.ПриОпределенииНастроекРегламентныхЗаданий
Процедура ПриОпределенииНастроекРегламентныхЗаданий(Настройки) Экспорт
	Зависимость = Настройки.Добавить();
	Зависимость.РегламентноеЗадание = Метаданные.РегламентныеЗадания.ИзвлечениеТекста;
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ПолнотекстовыйПоиск") Тогда
		МодульПолнотекстовыйПоискСервер = ОбщегоНазначения.ОбщийМодуль("ПолнотекстовыйПоискСервер");
		Зависимость.ФункциональнаяОпция = МодульПолнотекстовыйПоискСервер.ФункциональнаяОпцияИспользоватьПолнотекстовыйПоиск();
	КонецЕсли;
	Зависимость.ДоступноВМоделиСервиса = Ложь;
	
	Зависимость = Настройки.Добавить();
	Зависимость.РегламентноеЗадание = Метаданные.РегламентныеЗадания.ОчисткаНенужныхФайлов;
	Зависимость.РаботаетСВнешнимиРесурсами = Истина;
	
	Зависимость = Настройки.Добавить();
	Зависимость.РегламентноеЗадание = Метаданные.РегламентныеЗадания.СинхронизацияФайлов;
	Зависимость.ФункциональнаяОпция = Метаданные.ФункциональныеОпции.ИспользоватьСинхронизациюФайлов;
	Зависимость.РаботаетСВнешнимиРесурсами = Истина;
КонецПроцедуры

// См. УправлениеДоступомПереопределяемый.ПриЗаполненииСписковСОграничениемДоступа.
Процедура ПриЗаполненииСписковСОграничениемДоступа(Списки) Экспорт
	
	Списки.Вставить(Метаданные.Справочники.ВерсииФайлов, Истина);
	Списки.Вставить(Метаданные.Справочники.ПапкиФайлов, Истина);
	Списки.Вставить(Метаданные.Справочники.Файлы, Истина);
	
КонецПроцедуры

// См. УправлениеДоступомПереопределяемый.ПриЗаполненииВозможныхПравДляНастройкиПравОбъектов.
Процедура ПриЗаполненииВозможныхПравДляНастройкиПравОбъектов(ВозможныеПрава) Экспорт
	
	////////////////////////////////////////////////////////////
	// Справочник.ПапкиФайлов
	
	// Право "Чтение папок и файлов".
	Право = ВозможныеПрава.Добавить();
	Право.ВладелецПрав  = Метаданные.Справочники.ПапкиФайлов.ПолноеИмя();
	Право.Имя           = "Чтение";
	Право.Заголовок     = НСтр("ru = 'Чтение'");
	Право.Подсказка     = НСтр("ru = 'Чтение папок и файлов'");
	Право.НачальноеЗначение = Истина;
	// Права для стандартных шаблонов ограничений доступа.
	Право.ЧтениеВТаблицах.Добавить("*");
	
	// Право "Изменение папок"
	Право = ВозможныеПрава.Добавить();
	Право.ВладелецПрав  = Метаданные.Справочники.ПапкиФайлов.ПолноеИмя();
	Право.Имя           = "ИзменениеПапок";
	Право.Заголовок     = НСтр("ru = 'Изменение
	                                 |папок'");
	Право.Подсказка     = НСтр("ru = 'Добавление, изменение и
	                                 |пометка удаления папок файлов'");
	// Права, требуемые для этого права.
	Право.ТребуемыеПрава.Добавить("Чтение");
	// Права для стандартных шаблонов ограничений доступа.
	Право.ИзменениеВТаблицах.Добавить(Метаданные.Справочники.ПапкиФайлов.ПолноеИмя());
	
	// Право "Изменение файлов"
	Право = ВозможныеПрава.Добавить();
	Право.ВладелецПрав  = Метаданные.Справочники.ПапкиФайлов.ПолноеИмя();
	Право.Имя           = "ИзменениеФайлов";
	Право.Заголовок     = НСтр("ru = 'Изменение
	                                 |файлов'");
	Право.Подсказка     = НСтр("ru = 'Изменение файлов в папке'");
	// Права, требуемые для этого права.
	Право.ТребуемыеПрава.Добавить("Чтение");
	// Права для стандартных шаблонов ограничений доступа.
	Право.ИзменениеВТаблицах.Добавить("*");
	
	// Право "Добавление файлов"
	Право = ВозможныеПрава.Добавить();
	Право.ВладелецПрав  = Метаданные.Справочники.ПапкиФайлов.ПолноеИмя();
	Право.Имя           = "ДобавлениеФайлов";
	Право.Заголовок     = НСтр("ru = 'Добавление
	                                 |файлов'");
	Право.Подсказка     = НСтр("ru = 'Добавление файлов в папку'");
	// Права, требуемые для этого права.
	Право.ТребуемыеПрава.Добавить("ИзменениеФайлов");
	
	// Право "Пометка удаления файлов".
	Право = ВозможныеПрава.Добавить();
	Право.ВладелецПрав  = Метаданные.Справочники.ПапкиФайлов.ПолноеИмя();
	Право.Имя           = "ПометкаУдаленияФайлов";
	Право.Заголовок     = НСтр("ru = 'Пометка
	                                 |удаления'");
	Право.Подсказка     = НСтр("ru = 'Пометка удаления файлов в папке'");
	// Права, требуемые для этого права.
	Право.ТребуемыеПрава.Добавить("ИзменениеФайлов");
	
	Право = ВозможныеПрава.Добавить();
	Право.ВладелецПрав  = Метаданные.Справочники.ПапкиФайлов.ПолноеИмя();
	Право.Имя           = "УправлениеПравами";
	Право.Заголовок     = НСтр("ru = 'Управление
	                                 |правами'");
	Право.Подсказка     = НСтр("ru = 'Управление правами папки'");
	// Права, требуемые для этого права.
	Право.ТребуемыеПрава.Добавить("Чтение");
	
КонецПроцедуры

// См. УправлениеДоступомПереопределяемый.ПриЗаполненииВидовОграниченийПравОбъектовМетаданных.
Процедура ПриЗаполненииВидовОграниченийПравОбъектовМетаданных(Описание) Экспорт
	
	Описание = Описание + "
		|Справочник.ПапкиФайлов.Чтение.НастройкиПрав.Справочник.ПапкиФайлов
		|Справочник.ПапкиФайлов.Изменение.НастройкиПрав.Справочник.ПапкиФайлов
		|Справочник.Файлы.Чтение.НастройкиПрав.Справочник.ПапкиФайлов
		|Справочник.Файлы.Изменение.НастройкиПрав.Справочник.ПапкиФайлов
		|Справочник.Файлы.Изменение.ВнешниеПользователи
		|Справочник.Файлы.Чтение.ВнешниеПользователи
		|";
	
	ТипыВладельцевФайлов = Метаданные.ОпределяемыеТипы.ВладелецФайлов.Тип.Типы();
	Для Каждого ТипВладельца Из ТипыВладельцевФайлов Цикл
		
		МетаданныеВладельца = Метаданные.НайтиПоТипу(ТипВладельца);
		Если МетаданныеВладельца = Неопределено Тогда
			Продолжить;
		КонецЕсли;
		
		ПолноеИмяВладельца = МетаданныеВладельца.ПолноеИмя();
		
		Описание = Описание + "
			|Справочник.ВерсииФайлов.Чтение.Объект." + ПолноеИмяВладельца + "
			|Справочник.ВерсииФайлов.Изменение.Объект." + ПолноеИмяВладельца + "
			|Справочник.Файлы.Чтение.Объект." + ПолноеИмяВладельца + "
			|Справочник.Файлы.Изменение.Объект." + ПолноеИмяВладельца + "
			|";
		
	КонецЦикла;
	
КонецПроцедуры

// См. ОбновлениеИнформационнойБазыБСП.ПриДобавленииОбработчиковОбновления.
Процедура ПриДобавленииОбработчиковОбновления(Обработчики) Экспорт
	
	Обработчик = Обработчики.Добавить();
	Обработчик.Версия = "2.4.1.1";
	Обработчик.ОбщиеДанные = Истина;
	Обработчик.НачальноеЗаполнение = Истина;
	Обработчик.Процедура = "РаботаСФайламиСлужебный.ОбновитьСписокЗапрещенныхРасширений";
	Обработчик.РежимВыполнения = "Оперативно";
	
	Обработчик = Обработчики.Добавить();
	Обработчик.Версия = "2.4.1.1";
	Обработчик.НачальноеЗаполнение = Истина;
	Обработчик.Процедура = "РаботаСФайламиСлужебный.ОбновитьСписокЗапрещенныхРасширенийВОбластиДанных";
	Обработчик.РежимВыполнения = "Оперативно";
	
	Обработчик = Обработчики.Добавить();
	Обработчик.Версия = "3.1.3.119";
	Обработчик.ОбщиеДанные = Истина;
	Обработчик.НачальноеЗаполнение = Истина;
	Обработчик.Процедура = "РаботаСФайламиСлужебный.ОбновитьСписокРасширенийТекстовыхФайлов";
	Обработчик.РежимВыполнения = "Оперативно";
	
	ПодсистемаЭПСуществует = ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ЭлектроннаяПодпись");
	Если ПодсистемаЭПСуществует Тогда
		
		Обработчик = Обработчики.Добавить();
		Обработчик.Версия = "2.4.1.49";
		Обработчик.Комментарий =
			НСтр("ru = 'Перенос электронных подписей и сертификатов шифрования
			           |из табличных частей в регистры сведений.'");
		Обработчик.Идентификатор = Новый УникальныйИдентификатор("d70f378a-41f5-4b0a-a1a7-f4ba27c7f91b");
		Обработчик.Процедура = "РаботаСФайлами.ПеренестиЭлектронныеПодписиИСертификатыШифрованияВРегистрыСведений";
		Обработчик.РежимВыполнения = "Отложенно";
		Обработчик.ПроцедураЗаполненияДанныхОбновления = "РаботаСФайламиСлужебный.ЗарегистрироватьОбъектыДляПереносаЭлектронныхПодписейИСертификатовШифрования";
		Обработчик.ЧитаемыеОбъекты      = СтрСоединить(ПолныеИменаСправочниковПрисоединенныхФайлов(), ", ");
		Обработчик.ИзменяемыеОбъекты    = ИзменяемыеОбъектыПриПереносеЭлектронныхПодписейИСертификатовШифрования() + "," + СтрСоединить(ПолныеИменаСправочниковПрисоединенныхФайлов(), ", ");
		Обработчик.ПроцедураПроверки    = "ОбновлениеИнформационнойБазы.ДанныеОбновленыНаНовуюВерсиюПрограммы";
		Обработчик.ПриоритетыВыполнения = ОбновлениеИнформационнойБазы.ПриоритетыВыполненияОбработчика();
		Приоритет = Обработчик.ПриоритетыВыполнения.Добавить();
		Приоритет.Процедура = "РегистрыСведений.ДвоичныеДанныеФайлов.ОбработатьДанныеДляПереходаНаНовуюВерсию";
		Приоритет.Порядок = "До";
		Приоритет = Обработчик.ПриоритетыВыполнения.Добавить();
		Приоритет.Процедура = "РегистрыСведений.НаличиеФайлов.ОбработатьДанныеДляПереходаНаНовуюВерсию";
		Приоритет.Порядок = "Любой";
		Приоритет = Обработчик.ПриоритетыВыполнения.Добавить();
		Приоритет.Процедура = "РегистрыСведений.СведенияОФайлах.ОбработатьДанныеДляПереходаНаНовуюВерсию";
		Приоритет.Порядок = "Любой";
		Приоритет = Обработчик.ПриоритетыВыполнения.Добавить();
		Приоритет.Процедура = "Справочники.Файлы.ОбработатьДанныеДляПереходаНаНовуюВерсию";
		Приоритет.Порядок = "Любой";
		
	КонецЕсли;
	
	
	Обработчик = Обработчики.Добавить();
	Обработчик.Версия = "2.4.1.1";
	Обработчик.Комментарий =
			НСтр("ru = 'Перенос информации о наличии файлов в регистр сведений Наличие файлов.'");
	Обработчик.Идентификатор = Новый УникальныйИдентификатор("a84931bb-dfd5-4525-ab4a-1a0646e17334");
	Обработчик.Процедура = "РегистрыСведений.НаличиеФайлов.ОбработатьДанныеДляПереходаНаНовуюВерсию";
	Обработчик.РежимВыполнения = "Отложенно";
	Обработчик.ПроцедураЗаполненияДанныхОбновления = "РегистрыСведений.НаличиеФайлов.ЗарегистрироватьДанныеКОбработкеДляПереходаНаНовуюВерсию";
	Обработчик.ЧитаемыеОбъекты      = "Справочник.Файлы";
	Обработчик.ИзменяемыеОбъекты    = "Справочник.Файлы,РегистрСведений.НаличиеФайлов";
	Обработчик.БлокируемыеОбъекты   = "Справочник.Файлы";
	Обработчик.ПроцедураПроверки    = "ОбновлениеИнформационнойБазы.ДанныеОбновленыНаНовуюВерсиюПрограммы";
	Обработчик.ПриоритетыВыполнения = ОбновлениеИнформационнойБазы.ПриоритетыВыполненияОбработчика();
	
	Если ПодсистемаЭПСуществует Тогда
		Приоритет = Обработчик.ПриоритетыВыполнения.Добавить();
		Приоритет.Процедура = "РаботаСФайлами.ПеренестиЭлектронныеПодписиИСертификатыШифрованияВРегистрыСведений";
		Приоритет.Порядок = "Любой";
	КонецЕсли;
	
	Приоритет = Обработчик.ПриоритетыВыполнения.Добавить();
	Приоритет.Процедура = "РегистрыСведений.СведенияОФайлах.ОбработатьДанныеДляПереходаНаНовуюВерсию";
	Приоритет.Порядок = "Любой";
	
	Приоритет = Обработчик.ПриоритетыВыполнения.Добавить();
	Приоритет.Процедура = "Справочники.Файлы.ОбработатьДанныеДляПереходаНаНовуюВерсию";
	Приоритет.Порядок = "Любой";
	
	
	Обработчик = Обработчики.Добавить();
	Обработчик.Версия = "3.0.2.46";
	Обработчик.Комментарий =
			НСтр("ru = 'Обновление универсальной даты и типа хранения элементов справочника Файлы.'");
	Обработчик.Идентификатор = Новый УникальныйИдентификатор("8b417c47-dd46-45ce-b59b-c675059c9020");
	Обработчик.Процедура = "Справочники.Файлы.ОбработатьДанныеДляПереходаНаНовуюВерсию";
	Обработчик.РежимВыполнения = "Отложенно";
	Обработчик.ЧитаемыеОбъекты      = "Справочник.Файлы";
	Обработчик.ИзменяемыеОбъекты    = "Справочник.Файлы,РегистрСведений.СведенияОФайлах";
	Обработчик.БлокируемыеОбъекты   = "Справочник.Файлы,Справочник.ВерсииФайлов";
	Обработчик.ПроцедураЗаполненияДанныхОбновления = "Справочники.Файлы.ЗарегистрироватьДанныеКОбработкеДляПереходаНаНовуюВерсию";
	Обработчик.ПроцедураПроверки    = "ОбновлениеИнформационнойБазы.ДанныеОбновленыНаНовуюВерсиюПрограммы";
	Обработчик.ПриоритетыВыполнения = ОбновлениеИнформационнойБазы.ПриоритетыВыполненияОбработчика();
	Обработчик.Многопоточный = Истина;
	
	Если ПодсистемаЭПСуществует Тогда
		Приоритет = Обработчик.ПриоритетыВыполнения.Добавить();
		Приоритет.Процедура = "РаботаСФайлами.ПеренестиЭлектронныеПодписиИСертификатыШифрованияВРегистрыСведений";
		Приоритет.Порядок = "Любой";
	КонецЕсли;
	
	Приоритет = Обработчик.ПриоритетыВыполнения.Добавить();
	Приоритет.Процедура = "РегистрыСведений.НаличиеФайлов.ОбработатьДанныеДляПереходаНаНовуюВерсию";
	Приоритет.Порядок = "Любой";
	Приоритет = Обработчик.ПриоритетыВыполнения.Добавить();
	Приоритет.Процедура = "РегистрыСведений.СведенияОФайлах.ОбработатьДанныеДляПереходаНаНовуюВерсию";
	Приоритет.Порядок = "Любой";
	
	
	Обработчик = Обработчики.Добавить();
	Обработчик.Процедура = "РегистрыСведений.СведенияОФайлах.ОбработатьДанныеДляПереходаНаНовуюВерсию";
	Обработчик.Версия = "3.0.2.46";
	Обработчик.РежимВыполнения = "Отложенно";
	Обработчик.Идентификатор = Новый УникальныйИдентификатор("5137a43e-75aa-4a68-ba2f-525a3a646af8");
	Обработчик.Многопоточный = Истина;
	Обработчик.ПроцедураЗаполненияДанныхОбновления = "РегистрыСведений.СведенияОФайлах.ЗарегистрироватьДанныеКОбработкеДляПереходаНаНовуюВерсию";
	Обработчик.ПроцедураПроверки = "ОбновлениеИнформационнойБазы.ДанныеОбновленыНаНовуюВерсиюПрограммы";
	Обработчик.Комментарий = НСтр("ru = 'Перенос информации о файлах в регистр сведений Сведения о файлах.'");
	
	Читаемые = Новый Массив;
	Читаемые.Добавить(Метаданные.Справочники.Файлы.ПолноеИмя());
	Читаемые.Добавить(Метаданные.РегистрыСведений.СведенияОФайлах.ПолноеИмя());
	Обработчик.ЧитаемыеОбъекты = СтрСоединить(Читаемые, ",");
	
	Изменяемые = Новый Массив;
	Изменяемые.Добавить(Метаданные.РегистрыСведений.СведенияОФайлах.ПолноеИмя());
	Обработчик.ИзменяемыеОбъекты = СтрСоединить(Изменяемые, ",");
	
	Блокируемые = Новый Массив;
	Блокируемые.Добавить(Метаданные.Справочники.Файлы.ПолноеИмя());
	Блокируемые.Добавить(Метаданные.РегистрыСведений.СведенияОФайлах.ПолноеИмя());
	Обработчик.БлокируемыеОбъекты = СтрСоединить(Блокируемые, ",");
	
	Обработчик.ПриоритетыВыполнения = ОбновлениеИнформационнойБазы.ПриоритетыВыполненияОбработчика();

	Если ПодсистемаЭПСуществует Тогда
		НоваяСтрока = Обработчик.ПриоритетыВыполнения.Добавить();
		НоваяСтрока.Процедура = "РаботаСФайлами.ПеренестиЭлектронныеПодписиИСертификатыШифрованияВРегистрыСведений";
		НоваяСтрока.Порядок = "Любой";
	КонецЕсли;

	НоваяСтрока = Обработчик.ПриоритетыВыполнения.Добавить();
	НоваяСтрока.Процедура = "РегистрыСведений.ДвоичныеДанныеФайлов.ОбработатьДанныеДляПереходаНаНовуюВерсию";
	НоваяСтрока.Порядок = "До";

	НоваяСтрока = Обработчик.ПриоритетыВыполнения.Добавить();
	НоваяСтрока.Процедура = "РегистрыСведений.НаличиеФайлов.ОбработатьДанныеДляПереходаНаНовуюВерсию";
	НоваяСтрока.Порядок = "Любой";

	НоваяСтрока = Обработчик.ПриоритетыВыполнения.Добавить();
	НоваяСтрока.Процедура = "Справочники.Файлы.ОбработатьДанныеДляПереходаНаНовуюВерсию";
	НоваяСтрока.Порядок = "Любой";
	
	
	Обработчик = Обработчики.Добавить();
	Обработчик.Процедура = "РегистрыСведений.ДвоичныеДанныеФайлов.ОбработатьДанныеДляПереходаНаНовуюВерсию";
	Обработчик.Версия = "3.1.3.260";
	Обработчик.РежимВыполнения = "Отложенно";
	Обработчик.Идентификатор = Новый УникальныйИдентификатор("84e58943-94fe-4f92-99b3-91be534d3754");
	Обработчик.ПроцедураЗаполненияДанныхОбновления = "РегистрыСведений.ДвоичныеДанныеФайлов.ЗарегистрироватьДанныеКОбработкеДляПереходаНаНовуюВерсию";
	Обработчик.ПроцедураПроверки = "ОбновлениеИнформационнойБазы.ДанныеОбновленыНаНовуюВерсиюПрограммы";
	Обработчик.Комментарий = НСтр("ru = 'Создание недостающих версий файлов для элементов справочника Файлы.'");
	
	Читаемые = Новый Массив;
	Читаемые.Добавить(Метаданные.Справочники.Файлы.ПолноеИмя());
	Читаемые.Добавить(Метаданные.РегистрыСведений.ДвоичныеДанныеФайлов.ПолноеИмя());
	Обработчик.ЧитаемыеОбъекты = СтрСоединить(Читаемые, ",");
	
	Изменяемые = Новый Массив;
	Изменяемые.Добавить(Метаданные.Справочники.Файлы.ПолноеИмя());
	Изменяемые.Добавить(Метаданные.РегистрыСведений.ДвоичныеДанныеФайлов.ПолноеИмя());
	Изменяемые.Добавить(Метаданные.Справочники.ВерсииФайлов.ПолноеИмя());
	Обработчик.ИзменяемыеОбъекты = СтрСоединить(Изменяемые, ",");
	
	Блокируемые = Новый Массив;
	Блокируемые.Добавить(Метаданные.РегистрыСведений.ДвоичныеДанныеФайлов.ПолноеИмя());
	Блокируемые.Добавить(Метаданные.Справочники.ВерсииФайлов.ПолноеИмя());
	Блокируемые.Добавить(Метаданные.Справочники.Файлы.ПолноеИмя());
	Обработчик.БлокируемыеОбъекты = СтрСоединить(Блокируемые, ",");
	
	Обработчик.ПриоритетыВыполнения = ОбновлениеИнформационнойБазы.ПриоритетыВыполненияОбработчика();

	Если ПодсистемаЭПСуществует Тогда
		НоваяСтрока = Обработчик.ПриоритетыВыполнения.Добавить();
		НоваяСтрока.Процедура = "РаботаСФайлами.ПеренестиЭлектронныеПодписиИСертификатыШифрованияВРегистрыСведений";
		НоваяСтрока.Порядок = "После";
	КонецЕсли;

	НоваяСтрока = Обработчик.ПриоритетыВыполнения.Добавить();
	НоваяСтрока.Процедура = "РегистрыСведений.НаличиеФайлов.ОбработатьДанныеДляПереходаНаНовуюВерсию";
	НоваяСтрока.Порядок = "После";

	НоваяСтрока = Обработчик.ПриоритетыВыполнения.Добавить();
	НоваяСтрока.Процедура = "Справочники.Файлы.ОбработатьДанныеДляПереходаНаНовуюВерсию";
	НоваяСтрока.Порядок = "После";
	
	РаботаСФайламиВТомахСлужебный.ПриДобавленииОбработчиковОбновления(Обработчики);
	
КонецПроцедуры

// См. ОбщегоНазначенияПереопределяемый.ПриДобавленииПереименованийОбъектовМетаданных.
Процедура ПриДобавленииПереименованийОбъектовМетаданных(Итог) Экспорт
	
	Библиотека = "СтандартныеПодсистемы";
	
	СтароеИмя = "Роль.РаботаСПапкамиФайлов";
	НовоеИмя  = "Роль.ДобавлениеИзменениеПапокИФайлов";
	ОбщегоНазначения.ДобавитьПереименование(Итог, "2.4.1.1", СтароеИмя, НовоеИмя, Библиотека);
	
КонецПроцедуры

// См. ОбщегоНазначенияПереопределяемый.ПриДобавленииПараметровРаботыКлиента.
Процедура ПриДобавленииПараметровРаботыКлиента(Параметры) Экспорт
	
	Если Не ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		Возврат;
	КонецЕсли;
	
	НастройкиРаботыСФайлами = РаботаСФайламиСлужебныйПовтИсп.НастройкиРаботыСФайлами();
	
	Параметры.Вставить("ПерсональныеНастройкиРаботыСФайлами", Новый ФиксированнаяСтруктура(
		НастройкиРаботыСФайлами.ПерсональныеНастройки));
	
	Параметры.Вставить("ОбщиеНастройкиРаботыСФайлами", Новый ФиксированнаяСтруктура(
		НастройкиРаботыСФайлами.ОбщиеНастройки));
	
КонецПроцедуры

// См. ОбщегоНазначенияПереопределяемый.ПриДобавленииПараметровРаботыКлиентаПриЗапуске.
Процедура ПриДобавленииПараметровРаботыКлиентаПриЗапуске(Параметры) Экспорт
	
	Если Не ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		Возврат;
	КонецЕсли;
	
	НастройкиРаботыСФайлами = РаботаСФайламиСлужебныйПовтИсп.НастройкиРаботыСФайлами();
	
	Параметры.Вставить("ПерсональныеНастройкиРаботыСФайлами", Новый ФиксированнаяСтруктура(
		НастройкиРаботыСФайлами.ПерсональныеНастройки));
		
	КоличествоЗанятыхФайлов = 0;
	Если ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		Пользователь = Пользователи.АвторизованныйПользователь();
		Если ТипЗнч(Пользователь) = Тип("СправочникСсылка.Пользователи") Тогда
			КоличествоЗанятыхФайлов = КоличествоЗанятыхФайлов();
		КонецЕсли;
	КонецЕсли;
	
	Параметры.Вставить("КоличествоЗанятыхФайлов", КоличествоЗанятыхФайлов);
	
КонецПроцедуры

// См. РаботаВБезопасномРежимеПереопределяемый.ПриЗаполненииРазрешенийНаДоступКВнешнимРесурсам.
Процедура ПриЗаполненииРазрешенийНаДоступКВнешнимРесурсам(ЗапросыРазрешений) Экспорт
	
	Если ПолучитьФункциональнуюОпцию("ХранитьФайлыВТомахНаДиске") Тогда
		Справочники.ТомаХраненияФайлов.ДобавитьЗапросыНаИспользованиеВнешнихРесурсовВсехТомов(ЗапросыРазрешений);
	КонецЕсли;
	
КонецПроцедуры

// См. ВыгрузкаЗагрузкаДанныхПереопределяемый.ПриЗаполненииТиповОбщихДанныхНеТребующихСопоставлениеСсылокПриЗагрузке.
Процедура ПриЗаполненииТиповОбщихДанныхНеТребующихСопоставлениеСсылокПриЗагрузке(Типы) Экспорт
	
	// В процессе выгрузки данных ссылки на справочник ТомаХраненияФайлов очищаются,
	// а при загрузке загрузка производится относительно настройки томов в ИБ, в которую
	// производится загрузка, а не относительно настройки томов в ИБ, из которой были выгружены данные.
	Типы.Добавить(Метаданные.Справочники.ТомаХраненияФайлов);
	
КонецПроцедуры

// См. ВариантыОтчетовПереопределяемый.НастроитьВариантыОтчетов.
Процедура ПриНастройкеВариантовОтчетов(Настройки) Экспорт
	МодульВариантыОтчетов = ОбщегоНазначения.ОбщийМодуль("ВариантыОтчетов");
	МодульВариантыОтчетов.НастроитьОтчетВМодулеМенеджера(Настройки, Метаданные.Отчеты.ОбъемНенужныхФайлов);
	МодульВариантыОтчетов.НастроитьОтчетВМодулеМенеджера(Настройки, Метаданные.Отчеты.ПроверкаЦелостностиТома);
КонецПроцедуры

// См. ЦентрМониторингаПереопределяемый.ПриСбореПоказателейСтатистикиКонфигурации.
Процедура ПриСбореПоказателейСтатистикиКонфигурации() Экспорт
	
	Если Не ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ЦентрМониторинга") Тогда
		Возврат;
	КонецЕсли;
	
	МодульЦентрМониторинга = ОбщегоНазначения.ОбщийМодуль("ЦентрМониторинга");
	
	ТекстЗапроса = 
		"ВЫБРАТЬ
		|	КОЛИЧЕСТВО(1) КАК Количество
		|ИЗ
		|	Справочник.УчетныеЗаписиСинхронизацииФайлов КАК УчетныеЗаписиСинхронизацииФайлов";
	
	Запрос = Новый Запрос(ТекстЗапроса);
	Выборка = Запрос.Выполнить().Выбрать();
	
	МодульЦентрМониторинга.ЗаписатьСтатистикуОбъектаКонфигурации("Справочник.УчетныеЗаписиСинхронизацииФайлов", 
		Выборка.Количество());
	
	УстановитьПривилегированныйРежим(Истина);
	КлючиНастроек = ХранилищеОбщихНастроек.ПолучитьСписок("НастройкиСканирования/ИмяУстройства");
	Для Каждого КлючНастройки Из КлючиНастроек Цикл
		ИмяУстройства = ОбщегоНазначения.ХранилищеОбщихНастроекЗагрузить(
			"НастройкиСканирования/ИмяУстройства", 
			КлючНастройки, "");
		МодульЦентрМониторинга.ЗаписатьСтатистикуОбъектаКонфигурации("СканированиеИзображений.Сканер." + ИмяУстройства, 1);
	КонецЦикла;
КонецПроцедуры

// См. ПользователиПереопределяемый.ПриОпределенииНазначенияРолей
Процедура ПриОпределенииНазначенияРолей(НазначениеРолей) Экспорт
	
	// СовместноДляПользователейИВнешнихПользователей.
	НазначениеРолей.ТолькоДляВнешнихПользователей.Добавить(
		Метаданные.Роли.ДобавлениеИзменениеПапокИФайловВнешнимиПользователями.Имя);
	
КонецПроцедуры

// См. УправлениеСвойствамиПереопределяемый.ПриПолученииПредопределенныхНаборовСвойств.
Процедура ПриПолученииПредопределенныхНаборовСвойств(Наборы) Экспорт
	Набор = Наборы.Строки.Добавить();
	Набор.Имя = "Справочник_ПапкиФайлов";
	Набор.Идентификатор = Новый УникальныйИдентификатор("3f4bfd8d-b111-4416-8797-760b78f15910");
	
	Набор = Наборы.Строки.Добавить();
	Набор.Имя = "Справочник_Файлы";
	Набор.Идентификатор = Новый УникальныйИдентификатор("f85ae5e1-0ff9-4c97-b2bb-d0996eacc6cf");
КонецПроцедуры

// Для перехода с версий БСП 2.3.7 и младше. Устанавливает связь между
// подсистемой ПрисоединенныеФайлы и РаботаСФайлами.
//
Процедура ПриОпределенииНаследованияПодсистем(Выгрузка, НаследующиеПодсистемы) Экспорт
	
	ОтборПоУдаленным = Новый Структура;
	ОтборПоУдаленным.Вставить("Обновлен", Истина);
	ОтборПоУдаленным.Вставить("ПометкаУдаления", Истина);
	Удаленные = Выгрузка.НайтиСтроки(ОтборПоУдаленным);
	НаследующиеПодсистемы = Новый Массив;
	Для Каждого Удаленный Из Удаленные Цикл
		Если СтрНайти(Удаленный.ПолноеИмя, "Подсистема.СтандартныеПодсистемы.Подсистема.ПрисоединенныеФайлы") Тогда
			СтрокаРаботаСФайлами = Выгрузка.Найти("Подсистема.СтандартныеПодсистемы.Подсистема.РаботаСФайлами", "ПолноеИмя");
			Если СтрокаРаботаСФайлами <> Неопределено Тогда
				НаследующиеПодсистемы.Добавить(СтрокаРаботаСФайлами);
			КонецЕсли;
			
			Прервать;
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

// Смотри также ОбновлениеИнформационнойБазыПереопределяемый.ПриОпределенииНастроек
//
// Параметры:
//  Объекты - Массив из ОбъектМетаданных
//
Процедура ПриОпределенииОбъектовСНачальнымЗаполнением(Объекты) Экспорт
	
	Объекты.Добавить(Метаданные.Справочники.ПапкиФайлов);
	
КонецПроцедуры

// См. ОбменДаннымиПереопределяемый.ПриНастройкеПодчиненногоУзлаРИБ.
Процедура ПриНастройкеПодчиненногоУзлаРИБ() Экспорт

	РаботаСФайламиВТомахСлужебный.ЗаполнитьНастройкиХраненияФайлов();

КонецПроцедуры

// См. УдалениеПомеченныхОбъектовСлужебный.ЭтоТехническийОбъект
Функция ЭтоТехническийОбъект(ПолноеИмяОбъекта) Экспорт
	Возврат ПолноеИмяОбъекта = ВРег(Метаданные.Справочники.ВерсииФайлов.ПолноеИмя());
КонецФункции

// См. ИнтеграцияПодсистемБСП.ПослеДобавленияИзмененияПользователяИлиГруппы
Процедура ПослеДобавленияИзмененияПользователяИлиГруппы(Ссылка, ЭтоНовый) Экспорт

	ТипЗнч = ТипЗнч(Ссылка);
	ЭтоПользователь = ТипЗнч = Тип("СправочникСсылка.Пользователи")
						ИЛИ ТипЗнч = Тип("СправочникСсылка.ВнешниеПользователи");
	Если ЭтоНовый И ЭтоПользователь Тогда
		// Сбрасываем настройки рабочего каталога, если они скопированы от другого пользователя.
		РаботаСФайламиСлужебныйВызовСервера.УстановитьРабочийКаталогПользователя(Неопределено, Ссылка);
	КонецЕсли;

КонецПроцедуры

// см. УдалениеПомеченныхОбъектовПереопределяемый.ПередУдалениемГруппыОбъектов
Процедура ПередУдалениемГруппыОбъектов(Контекст, УдаляемыеОбъекты) Экспорт
	Контекст.Вставить("УдаляемыеФайлы", Новый Массив);
	
	Для Каждого УдаляемыйОбъект Из УдаляемыеОбъекты Цикл
		Если НЕ Метаданные.ОпределяемыеТипы.ПрисоединенныйФайл.Тип.СодержитТип(ТипЗнч(УдаляемыйОбъект)) Тогда
			Продолжить;
		КонецЕсли;
		
		ПрисоединенныйФайл = УдаляемыйОбъект; // ОпределяемыйТип.ПрисоединенныйФайл
		ТипХраненияФайлов = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(ПрисоединенныйФайл, "ТипХраненияФайла");
		Если ТипХраненияФайлов = Перечисления.ТипыХраненияФайлов.ВТомахНаДиске Тогда
			СвойстваФайла = РаботаСФайламиВТомахСлужебный.СвойстваФайлаВТоме(ПрисоединенныйФайл);
			ПолноеИмяФайла = РаботаСФайламиВТомахСлужебный.ПолноеИмяФайлаВТоме(СвойстваФайла);
			УдаляемыеФайлы = Контекст.УдаляемыеФайлы; // Массив из Строка
			УдаляемыеФайлы.Добавить(ПолноеИмяФайла);
		КонецЕсли;
	КонецЦикла;
КонецПроцедуры

// см. УдалениеПомеченныхОбъектовПереопределяемый.ПослеУдаленияГруппыОбъектов
Процедура ПослеУдаленияГруппыОбъектов(Контекст, Успешно) Экспорт
	Если Не Успешно Тогда
		Возврат;
	КонецЕсли;
	
	Для Каждого Файл Из Контекст.УдаляемыеФайлы Цикл
		РаботаСФайламиВТомахСлужебный.УдалитьФайл(Файл);
	КонецЦикла;
КонецПроцедуры

#КонецОбласти

#КонецОбласти

#Область СлужебныеПроцедурыИФункции

#Область ОчисткаНенужныхФайлов

Процедура ОчиститьФайлыПоНастройке(Настройка, МассивИсключений)
	Если Настройка.Действие = Перечисления.ВариантыОчисткиФайлов.НеОчищать Тогда
		Возврат;
	КонецЕсли;
	
	НенужныеФайлы = ВыбратьНенужныеФайлы(Настройка, МассивИсключений);
	МассивИсключений.Добавить(Настройка.ВладелецФайла);
	ОчиститьДанныеНенужныхФайлов(НенужныеФайлы);
КонецПроцедуры

// Параметры:
//  ВладелецФайла - СправочникСсылка.ИдентификаторыОбъектовМетаданных
//  Настройка - см. РегистрыСведений.НастройкиОчисткиФайлов.ТекущиеНастройкиОчистки
//  МассивИсключений - Массив из ОпределяемыйТип.ВладелецФайлов
//  ЭлементИсключение -  Тип - тип ОпределяемыйТип.ВладелецФайлов.
// 
// Возвращаемое значение:
//  Строка
//
Функция ТекстЗапросаДляОчисткиФайлов(ВладелецФайла, Настройка, МассивИсключений, ЭлементИсключение)
	
	РеквизитыСправочникаФайлов = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(Настройка.ТипВладельцаФайла, "ПолноеИмя, Имя");
	
	МетаданныеВладельцаФайлов = ОбщегоНазначения.ОбъектМетаданныхПоИдентификатору(ВладелецФайла);
	ПолноеИмяВладельцаФайлов = МетаданныеВладельцаФайлов.ПолноеИмя();
	
	МетаданныеОбъектаФайлов = ОбщегоНазначения.ОбъектМетаданныхПоИдентификатору(Настройка.ТипВладельцаФайла);
	ЕстьВозможностьХранитьВерсии = ОбщегоНазначения.ЕстьРеквизитОбъекта("ТекущаяВерсия", МетаданныеОбъектаФайлов);
	Если ЕстьВозможностьХранитьВерсии Тогда
		СправочникВерсийФайлов = ОбщегоНазначения.ИдентификаторОбъектаМетаданных(МетаданныеОбъектаФайлов.Реквизиты.ТекущаяВерсия.Тип.Типы()[0]);
		МетаданныеВерсийФайлов = ОбщегоНазначения.ОбъектМетаданныхПоИдентификатору(СправочникВерсийФайлов);
		ПолноеИмяСправочникаВерсийФайлов = МетаданныеВерсийФайлов.ПолноеИмя();
		
		Если Настройка.ПериодОчистки <> Перечисления.ПериодОчисткиФайлов.ПоПравилу Тогда
			ТекстЗапроса = 
				"ВЫБРАТЬ 
				|	ТИПЗНАЧЕНИЯ(Файлы.ВладелецФайла) КАК ВладелецФайла,
				|	ВерсииФайлов.Размер / (1024 * 1024) КАК Размер,
				|	Файлы.Ссылка КАК ФайлСсылка,
				|	ВерсииФайлов.Ссылка КАК ВерсияСсылка
				|ИЗ
				|	#ПолноеИмяСправочникаФайлов КАК Файлы
				|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ #ПолноеИмяСправочникаВерсийФайлов КАК ВерсииФайлов
				|		ПО Файлы.Ссылка = ВерсииФайлов.Владелец
				|ГДЕ
				|	ВерсииФайлов.ДатаСоздания <= &ПериодОчистки
				|	И НЕ Файлы.ПометкаУдаления
				|	И &ЭтоНеГруппа
				|	И ТИПЗНАЧЕНИЯ(Файлы.ВладелецФайла) = &ТипВладельца
				|	И ВЫБОР
				|			КОГДА ВерсииФайлов.ТипХраненияФайла = ЗНАЧЕНИЕ(Перечисление.ТипыХраненияФайлов.ВТомахНаДиске)
				|				ТОГДА ВерсииФайлов.Том <> ЗНАЧЕНИЕ(Справочник.ТомаХраненияФайлов.ПустаяСсылка)
				|						ИЛИ (ВЫРАЗИТЬ(ВерсииФайлов.ПутьКФайлу КАК СТРОКА(100))) <> """"
				|			ИНАЧЕ ИСТИНА
				|		КОНЕЦ
				|	";

			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#ПолноеИмяСправочникаФайлов", РеквизитыСправочникаФайлов.ПолноеИмя);
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#ПолноеИмяСправочникаВерсийФайлов", ПолноеИмяСправочникаВерсийФайлов);
		Иначе
			
			ТекстЗапроса = 
				"ВЫБРАТЬ 
				|	СправочникВладелецФайла.Ссылка,
				|	&Реквизиты,
				|	ТИПЗНАЧЕНИЯ(Файлы.ВладелецФайла) КАК ВладелецФайла,
				|	ВерсииФайлов.Размер / (1024 * 1024) КАК Размер,
				|	Файлы.Ссылка КАК ФайлСсылка,
				|	ВерсииФайлов.Ссылка КАК ВерсияСсылка
				|ИЗ
				|	#СправочникВладелецФайла КАК СправочникВладелецФайла
				|	ВНУТРЕННЕЕ СОЕДИНЕНИЕ #ПолноеИмяСправочникаФайлов КАК Файлы
				|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ #ПолноеИмяСправочникаВерсийФайлов КАК ВерсииФайлов
				|		ПО Файлы.Ссылка = ВерсииФайлов.Владелец
				|	ПО СправочникВладелецФайла.Ссылка = Файлы.ВладелецФайла
				|ГДЕ
				|	НЕ Файлы.ПометкаУдаления
				|	И &ЭтоНеГруппа
				|	И НЕ ЕСТЬNULL(ВерсииФайлов.ПометкаУдаления, ЛОЖЬ)
				|	И ВЫБОР
				|			КОГДА ВерсииФайлов.ТипХраненияФайла = ЗНАЧЕНИЕ(Перечисление.ТипыХраненияФайлов.ВТомахНаДиске)
				|				ТОГДА ВерсииФайлов.Том <> ЗНАЧЕНИЕ(Справочник.ТомаХраненияФайлов.ПустаяСсылка)
				|						ИЛИ (ВЫРАЗИТЬ(ВерсииФайлов.ПутьКФайлу КАК СТРОКА(100))) <> """"
				|			ИНАЧЕ ИСТИНА
				|		КОНЕЦ
				|	И ТИПЗНАЧЕНИЯ(Файлы.ВладелецФайла) = &ТипВладельца
				|";	
			
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&Реквизиты" + ",", РеквизитыВладельцаФайлов(ВладелецФайла));
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#ПолноеИмяСправочникаВерсийФайлов", ПолноеИмяСправочникаВерсийФайлов);
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#ПолноеИмяСправочникаФайлов", РеквизитыСправочникаФайлов.ПолноеИмя);
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#СправочникВладелецФайла", ПолноеИмяВладельцаФайлов);
		КонецЕсли;
	
	Иначе // Не ЕстьВозможностьХранитьВерсии
	
		Если Настройка.ПериодОчистки <> Перечисления.ПериодОчисткиФайлов.ПоПравилу Тогда
			ТекстЗапроса = 
			"ВЫБРАТЬ
			|	ТИПЗНАЧЕНИЯ(Файлы.ВладелецФайла) КАК ВладелецФайла,
			|	Файлы.Размер / (1024 * 1024) КАК Размер,
			|	Файлы.Ссылка КАК ФайлСсылка
			|ИЗ
			|	#ТипВладельцаФайла КАК Файлы
			|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ #СправочникВладелецФайла КАК СправочникВладелецФайла
			|		ПО Файлы.ВладелецФайла = СправочникВладелецФайла.Ссылка
			|ГДЕ
			|	Файлы.ДатаСоздания <= &ПериодОчистки
			|	И НЕ Файлы.ПометкаУдаления
			|	И &ЭтоНеГруппа
			|	И ВЫБОР
			|			КОГДА Файлы.ТипХраненияФайла = ЗНАЧЕНИЕ(Перечисление.ТипыХраненияФайлов.ВТомахНаДиске)
			|				ТОГДА (ВЫРАЗИТЬ(Файлы.ПутьКФайлу КАК СТРОКА(100))) <> """"
			|						ИЛИ НЕ Файлы.Том = ЗНАЧЕНИЕ(Справочник.ТомаХраненияФайлов.ПустаяСсылка)
			|			ИНАЧЕ ИСТИНА
			|		КОНЕЦ
			|	И ТИПЗНАЧЕНИЯ(Файлы.ВладелецФайла) = &ТипВладельца
			|	";

			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#ТипВладельцаФайла", "Справочник." + РеквизитыСправочникаФайлов.Имя);
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#СправочникВладелецФайла", ПолноеИмяВладельцаФайлов);
		Иначе
			
			ТекстЗапроса = 
			"ВЫБРАТЬ
			|	СправочникВладелецФайла.Ссылка,
			|	ТИПЗНАЧЕНИЯ(Файлы.ВладелецФайла) КАК ВладелецФайла,
			|	Файлы.Размер / (1024 * 1024) КАК Размер,
			|	&Реквизиты,
			|	Файлы.Ссылка КАК ФайлСсылка
			|ИЗ
			|	#ПолноеИмяСправочникаФайлов КАК Файлы
			|		ЛЕВОЕ СОЕДИНЕНИЕ #СправочникВладелецФайла КАК СправочникВладелецФайла
			|		ПО Файлы.ВладелецФайла = СправочникВладелецФайла.Ссылка
			|ГДЕ
			|	НЕ Файлы.ПометкаУдаления
			|	И &ЭтоНеГруппа
			|	И ВЫБОР
			|			КОГДА Файлы.ТипХраненияФайла = ЗНАЧЕНИЕ(Перечисление.ТипыХраненияФайлов.ВТомахНаДиске)
			|				ТОГДА (ВЫРАЗИТЬ(Файлы.ПутьКФайлу КАК СТРОКА(100))) <> """"
			|						ИЛИ НЕ Файлы.Том = ЗНАЧЕНИЕ(Справочник.ТомаХраненияФайлов.ПустаяСсылка)
			|			ИНАЧЕ ИСТИНА
			|		КОНЕЦ
			|	И ТИПЗНАЧЕНИЯ(Файлы.ВладелецФайла) = &ТипВладельца";
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&Реквизиты" + ",", РеквизитыВладельцаФайлов(ВладелецФайла));
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#ПолноеИмяСправочникаФайлов", РеквизитыСправочникаФайлов.ПолноеИмя);
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#СправочникВладелецФайла", ПолноеИмяВладельцаФайлов);
		КонецЕсли;
	КонецЕсли;
	
	Если МассивИсключений.Количество() > 0 Тогда
		ТекстЗапроса = ТекстЗапроса + "
		|	И НЕ Файлы.ВладелецФайла В ИЕРАРХИИ (&МассивИсключений)"; // @query-part
	КонецЕсли;
	Если ЭлементИсключение <> Неопределено Тогда
		ТекстЗапроса = ТекстЗапроса + "
		|	И Файлы.ВладелецФайла В ИЕРАРХИИ (&ЭлементИсключение)"; // @query-part
	КонецЕсли;
	Если ЕстьВозможностьХранитьВерсии И Настройка.Действие = Перечисления.ВариантыОчисткиФайлов.ОчиститьВерсии Тогда
		ТекстЗапроса =  ТекстЗапроса + "
		|	И ВерсииФайлов.Ссылка <> Файлы.ТекущаяВерсия
		|	И ВерсииФайлов.РодительскаяВерсия <> ЗНАЧЕНИЕ(Справочник.ВерсииФайлов.ПустаяСсылка)"; // @query-part
	КонецЕсли;
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ЭтоНеГруппа", 
		?(МетаданныеОбъектаФайлов.Иерархический, "НЕ Файлы.ЭтоГруппа", "ИСТИНА")); // @query-part
	
	Возврат ТекстЗапроса;
	
КонецФункции

// Возвращает режим очистки файлов.
// 
// Возвращаемое значение:
//  ПеречислениеСсылка.РежимыОчисткиФайлов
//
Функция РежимОчисткиФайлов() Экспорт
	Режим = Константы.РежимОчисткиФайлов.Получить();
	Если Не ЗначениеЗаполнено(Режим) Тогда
		Отбор = Новый Структура;
		Отбор.Вставить("Метаданные", Метаданные.РегламентныеЗадания.ОчисткаНенужныхФайлов);
		Задание = РегламентныеЗаданияСервер.НайтиЗадания(Отбор);
		Если Задание.Количество() > 0 Тогда
			Задание = Задание[0];
			Режим = ?(Задание.Использование, Перечисления.РежимыОчисткиФайлов.ОчищатьУдаленныеИНенужные, Перечисления.РежимыОчисткиФайлов.НеОчищать);
		Иначе
			Режим = Перечисления.РежимыОчисткиФайлов.НеОчищать;
		КонецЕсли;
	КонецЕсли;
	
	Возврат Режим;
КонецФункции

// Устанавливает значение константы РежимОчисткиФайлов и использование регламентного задания.
// 
// Параметры:
//  Режим - ПеречислениеСсылка.РежимыОчисткиФайлов
//
Процедура УстановитьРежимОчисткиФайлов(Режим) Экспорт
	АвтоматическиОчищатьНенужныеФайлы = ?(Режим = Перечисления.РежимыОчисткиФайлов.НеОчищать, Ложь, Истина);
	Константы.РежимОчисткиФайлов.Установить(Режим);
	
	ПараметрыЗадания = Новый Структура;
	ПараметрыЗадания.Вставить("Метаданные", Метаданные.РегламентныеЗадания.ОчисткаНенужныхФайлов);
	Если Не ОбщегоНазначения.РазделениеВключено() Тогда
		ПараметрыЗадания.Вставить("ИмяМетода", Метаданные.РегламентныеЗадания.ОчисткаНенужныхФайлов.ИмяМетода);
	КонецЕсли;
	
	УстановитьПривилегированныйРежим(Истина);
	
	СписокЗаданий = РегламентныеЗаданияСервер.НайтиЗадания(ПараметрыЗадания);
	ИмяПараметра = "Использование";
	Если СписокЗаданий.Количество() = 0 Тогда
		ПараметрыЗадания.Вставить(ИмяПараметра, АвтоматическиОчищатьНенужныеФайлы);
		РегламентныеЗаданияСервер.ДобавитьЗадание(ПараметрыЗадания);
	Иначе
		ПараметрыЗадания = Новый Структура(ИмяПараметра, АвтоматическиОчищатьНенужныеФайлы);
		Для Каждого Задание Из СписокЗаданий Цикл
			РегламентныеЗаданияСервер.ИзменитьЗадание(Задание, ПараметрыЗадания);
		КонецЦикла;
	КонецЕсли;
КонецПроцедуры

Процедура ЗаписатьВЖурналСобытийОчисткиФайлов(Знач ТекстСообщения, Знач Уровень = Неопределено,
	ПрисоединенныйФайл = Неопределено)

	Если Уровень = Неопределено Тогда
		Уровень = УровеньЖурналаРегистрации.Информация;
	КонецЕсли;
	ЗаписьЖурналаРегистрации(СобытиеЖурналаРегистрацииОчисткаФайлов(), 
		Уровень, ?(ПрисоединенныйФайл <> Неопределено, ПрисоединенныйФайл.Метаданные(), Неопределено),
		ПрисоединенныйФайл, ТекстСообщения);
	
КонецПроцедуры

Функция СобытиеЖурналаРегистрацииОчисткаФайлов()
	
	Возврат НСтр("ru = 'Файлы.Очистка файлов'", ОбщегоНазначения.КодОсновногоЯзыка());
	
КонецФункции

#КонецОбласти

// Двоичные данные из сведений о файле.
// 
// Параметры:
//  СведенияОФайле - см. РаботаСФайламиКлиентСервер.СведенияОФайле
// 
// Возвращаемое значение:
//  ДвоичныеДанные
//
Функция ДвоичныеДанныеИзСведенийОФайле(СведенияОФайле) Экспорт
	ТипЗнч = ТипЗнч(СведенияОФайле.АдресВременногоХранилищаФайла);
	Если ТипЗнч = Тип("ДвоичныеДанные") Тогда
		Возврат СведенияОФайле.АдресВременногоХранилищаФайла;
	ИначеЕсли ЭтоАдресВременногоХранилища(СведенияОФайле.АдресВременногоХранилищаФайла) Тогда
		Возврат ПолучитьИзВременногоХранилища(СведенияОФайле.АдресВременногоХранилищаФайла);
	Иначе
		ВызватьИсключение НСтр("ru='Не поддерживаемый тип хранилища файлов.'");
	КонецЕсли;
КонецФункции

// Сведения об очищаемых файлах.
// 
// Возвращаемое значение:
//  Структура:
//   * ОбъемУдаляемыхФайлов - Число - в Мб, результат отчета, см. Отчеты.ПроверкаЦелостностиТома.
//   * ОбъемНенужныхФайлов - Число - в Мб, результат отчета, см. Отчеты.ОбъемНенужныхФайлов.
//
Функция СведенияОбОчищаемыхФайлах() Экспорт
	Результат = Новый Структура;
	Результат.Вставить("ОбъемУдаляемыхФайлов", 0);
	Результат.Вставить("ОбъемНенужныхФайлов", 0);
	
	ТаблицаНенужныхФайлов = Отчеты.ОбъемНенужныхФайлов.ТаблицаНенужныхФайлов();
	ТаблицаНенужныхФайлов.Свернуть(,"ОбъемНенужныхФайлов");
	Результат.ОбъемНенужныхФайлов = ?(ТаблицаНенужныхФайлов.Количество() > 0, 
		ТаблицаНенужныхФайлов[0].ОбъемНенужныхФайлов, 0);
	
	Тома = РаботаСФайламиВТомахСлужебный.ДоступныеТома();
	ЛишниеФайлы = РаботаСФайламиВТомахСлужебный.ЛишниеФайлыНаДиске();
	Для Каждого Том Из Тома Цикл
		РаботаСФайламиВТомахСлужебный.ЗаполнитьЛишниеФайлы(ЛишниеФайлы, Том);
	КонецЦикла;
	
	Для Каждого ЛишнийФайл Из ЛишниеФайлы Цикл
		Файл = Новый Файл(ЛишнийФайл.ПолноеИмя);
		
		Если Файл.Существует() Тогда
			Результат.ОбъемУдаляемыхФайлов = Результат.ОбъемУдаляемыхФайлов + Файл.Размер();
		КонецЕсли;
	КонецЦикла;
	
	Результат.ОбъемУдаляемыхФайлов = Результат.ОбъемУдаляемыхФайлов / 1024 / 1024;
	
	Возврат Результат;
КонецФункции

// Возвращает путь к рабочему каталогу пользователя в настройках.
//
// Возвращаемое значение:
//  Строка - имя каталога.
//
Функция РабочийКаталогПользователя()
	
	УстановитьПривилегированныйРежим(Истина);
	ИмяКаталога = ОбщегоНазначения.ХранилищеОбщихНастроекЗагрузить("ЛокальныйКэшФайлов", "ПутьКЛокальномуКэшуФайлов");
	Если ИмяКаталога = Неопределено Тогда
		ИмяКаталога = "";
	КонецЕсли;
	
	Возврат ИмяКаталога;
	
КонецФункции

// Возвращает навигационную ссылку на файл (на реквизит или во временное хранилище).
//
// Параметры:
//  ФайлСсылка             -  ОпределяемыйТип.ПрисоединенныйФайл
//  УникальныйИдентификатор - УникальныйИдентификатор
// 
// Возвращаемое значение:
//   см. РаботаСФайламиСлужебныйВызовСервера.ПолучитьНавигационнуюСсылкуДляОткрытия
//
Функция НавигационнойСсылкиФайла(ФайлСсылка, УникальныйИдентификатор) Экспорт
	
	Если ЭтоЭлементРаботаСФайлами(ФайлСсылка) Тогда
		Возврат РаботаСФайламиСлужебныйВызовСервера.ПолучитьНавигационнуюСсылкуДляОткрытия(ФайлСсылка, УникальныйИдентификатор);
	КонецЕсли;
	
	Возврат Неопределено;
	
КонецФункции

// Обработчик подписки "при записи" присоединенного файла.
//
Процедура ПриЗаписиПрисоединенногоФайлаСервер(ВладелецФайлов, Источник) Экспорт
	
	УстановитьПривилегированныйРежим(Истина);
	НачатьТранзакцию();
	Попытка
	
		ЗаписьИзменилась = Ложь;
		
		БлокировкаДанных = Новый БлокировкаДанных;
		ЭлементБлокировкиДанных = БлокировкаДанных.Добавить(Метаданные.РегистрыСведений.НаличиеФайлов.ПолноеИмя());
		ЭлементБлокировкиДанных.УстановитьЗначение("ОбъектСФайлами", ВладелецФайлов);
		БлокировкаДанных.Заблокировать();
		
		МенеджерЗаписи = РегистрыСведений.НаличиеФайлов.СоздатьМенеджерЗаписи();
		МенеджерЗаписи.ОбъектСФайлами = ВладелецФайлов;
		МенеджерЗаписи.Прочитать();
		
		Если НЕ ЗначениеЗаполнено(МенеджерЗаписи.ОбъектСФайлами) Тогда
			МенеджерЗаписи.ОбъектСФайлами = ВладелецФайлов;
			ЗаписьИзменилась = Истина;
		КонецЕсли;
		
		ЕстьФайлы = НЕ Источник.ПометкаУдаления ИЛИ ЕстьФайлыУВладельца(ВладелецФайлов);
		Если МенеджерЗаписи.ЕстьФайлы <> ЕстьФайлы Тогда
			МенеджерЗаписи.ЕстьФайлы = ЕстьФайлы;
			ЗаписьИзменилась = Истина;
		КонецЕсли;
		
		Если ПустаяСтрока(МенеджерЗаписи.ИдентификаторОбъекта) Тогда
			МенеджерЗаписи.ИдентификаторОбъекта = ПолучитьОчереднойИдентификаторОбъекта();
			ЗаписьИзменилась = Истина;
		КонецЕсли;
		
		Если ЗаписьИзменилась Тогда
			МенеджерЗаписи.Записать();
		КонецЕсли;
		
		Если Не Источник.ЭтоГруппа Тогда
			МенеджерЗаписи = РегистрыСведений.СведенияОФайлах.СоздатьМенеджерЗаписи();
			ЗаполнитьЗначенияСвойств(МенеджерЗаписи, Источник);
			МенеджерЗаписи.Файл = Источник.Ссылка;
			Если Источник.ПодписанЭП И Источник.Зашифрован Тогда
				МенеджерЗаписи.НомерКартинкиПодписанЗашифрован = 2;
			ИначеЕсли Источник.Зашифрован Тогда
				МенеджерЗаписи.НомерКартинкиПодписанЗашифрован = 1;
			ИначеЕсли Источник.ПодписанЭП Тогда
				МенеджерЗаписи.НомерКартинкиПодписанЗашифрован = 0;
			Иначе
				МенеджерЗаписи.НомерКартинкиПодписанЗашифрован = -1;
			КонецЕсли;
			
			МенеджерЗаписи.Записать();
		КонецЕсли;
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

// Для передачи пути каталога файлов в обработчик ПриОтправкеДанныхФайла.
//
Процедура СохранитьНастройку(КлючОбъекта, КлючНастроек, Настройки) 
	
	УстановитьПривилегированныйРежим(Истина);
	ХранилищеОбщихНастроек.Сохранить(КлючОбъекта, КлючНастроек, Настройки);
	
КонецПроцедуры

// Только для внутреннего использования.
//
// Параметры:
//   ЭлементДанных - ДокументОбъект
//                 - СправочникОбъект
//                 - ПланВидовРасчетаОбъект
//                 - ПланВидовХарактеристикОбъект
//                 - РегистрСведенийНаборЗаписей
//                 - РегистрНакопленияНаборЗаписей
//                 - РегистрБухгалтерииНаборЗаписей
//                 - БизнесПроцессОбъект
//                 - ЗадачаОбъект
//   ОтправкаЭлемента - ОтправкаЭлементаДанных
//  СозданиеНачальногоОбраза - Булево
//   Получатель - ПланОбменаСсылка
//
Процедура ПриОтправкеФайла(ЭлементДанных, ОтправкаЭлемента, Знач СозданиеНачальногоОбраза = Ложь, Получатель = Неопределено)
	
	// Для обменов не РИБ используется алгоритм обычного сеанса обмена, а не создания начального образа,
	// т.к. параметра СозданиеНачальногоОбраза равный Истина означает начальную выгрузку данных.
	Если СозданиеНачальногоОбраза И Получатель <> Неопределено 
		И Не ЭтоУзелРаспределеннойИнформационнойБазы(Получатель.Ссылка) Тогда
		СозданиеНачальногоОбраза = Ложь;
	КонецЕсли;
	
	Если ОтправкаЭлемента = ОтправкаЭлементаДанных.Удалить
		ИЛИ ОтправкаЭлемента = ОтправкаЭлементаДанных.Игнорировать Тогда
		
		// Стандартную обработку не переопределяем.
		
	Иначе
		
		Если СозданиеНачальногоОбраза Тогда
			ПриОтправкеФайлаСозданиеНачальногоОбраза(ЭлементДанных, ОтправкаЭлемента, Получатель);
		Иначе	
			ПриОтправкеФайлаСообщениеОбмена(ЭлементДанных, ОтправкаЭлемента, Получатель);
		КонецЕсли;
		
	КонецЕсли
		
КонецПроцедуры

// Параметры:
//   ЭлементДанных - см. ПриОтправкеФайла.ЭлементДанных
//   ОтправкаЭлемента - см. ПриОтправкеФайла.ОтправкаЭлемента
//   Получатель - см. ПриОтправкеФайла.Получатель
//
Процедура ПриОтправкеФайлаСозданиеНачальногоОбраза(ЭлементДанных, ОтправкаЭлемента, Получатель);

	// При формировании начального образа нельзя изменять выгружаемые объекты.
	// Поэтому выгружаем файлы в отдельный том.
	// Если они не хранятся в базе.
	
	ТипФайла = ТипЗнч(ЭлементДанных);
	ЭтоЭлементРаботаСФайлами = ЭтоЭлементРаботаСФайлами(ЭлементДанных);

	Если ТипФайла = Тип("РегистрСведенийНаборЗаписей.ДвоичныеДанныеФайлов") Тогда
			
		// Хранение файлов в базе
		// Стандартную обработку не переопределяем.
		Возврат;
		
	ИначеЕсли ЭтоЭлементРаботаСФайлами 
			И ТипФайла <> Тип("СправочникОбъект.ИдентификаторыОбъектовМетаданных") Тогда
			
		Если ЭлементДанных.ТипХраненияФайла <> Перечисления.ТипыХраненияФайлов.ВТомахНаДиске Тогда
			
		// Хранение файлов в базе
		// Стандартную обработку не переопределяем.

			Возврат;
		Иначе
			
			// Файлы с версиями
			// Файлы в справочниках ПрисоединенныеФайлы
			// Копируем файл из тома в каталог создания начального образа.
			НовыйПутьФайла = ОбщегоНазначенияКлиентСервер.ПолучитьПолноеИмяФайла(
								Строка(ХранилищеОбщихНастроек.Загрузить("ОбменФайлами", "ВременныйКаталог")),
								ЭлементДанных.Ссылка.Метаданные().Имя + "." + ЭлементДанных.Ссылка.УникальныйИдентификатор());
				
			Попытка
				// Данные файла могут быть очищены
				РаботаСФайламиВТомахСлужебный.СкопироватьФайл(ЭлементДанных.Ссылка, НовыйПутьФайла);
			Исключение
				СообщениеОбОшибке = НСтр("ru='Не удалось скопировать данные файла во временный каталог.'") 
									+ Символы.ПС + Символы.ПС 
									+ ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
									
				ЗаписьЖурналаРегистрации(СобытиеЖурналаРегистрацииДляОбмена(), 
					УровеньЖурналаРегистрации.Предупреждение, 
					ЭлементДанных.Ссылка.Метаданные(), 
					ЭлементДанных.Ссылка, 
					СообщениеОбОшибке);
			КонецПопытки;
		КонецЕсли;
	КонецЕсли;

КонецПроцедуры

// Параметры:
//   ЭлементДанных - см. ПриОтправкеФайла.ЭлементДанных
//   ОтправкаЭлемента - см. ПриОтправкеФайла.ОтправкаЭлемента
//   Получатель - см. ПриОтправкеФайла.Получатель
//
Процедура ПриОтправкеФайлаСообщениеОбмена(ЭлементДанных, ОтправкаЭлемента, Получатель);

	Если ТипЗнч(ЭлементДанных) = Тип("РегистрСведенийНаборЗаписей.ДвоичныеДанныеФайлов") Тогда
		// Хранение файлов в регистре
		// Выгрузку регистра выполняем только при создании начального образа.
		ОтправкаЭлемента = ОтправкаЭлементаДанных.Игнорировать;

	ИначеЕсли ЭтоЭлементРаботаСФайлами(ЭлементДанных)
			И ТипЗнч(ЭлементДанных) <> Тип("СправочникОбъект.ИдентификаторыОбъектовМетаданных") Тогда
			
		ОбработатьОтправкуФайлаПоТипуХранения(ЭлементДанных);
	КонецЕсли;

КонецПроцедуры

// Только для внутреннего использования.
//
// Параметры:
//   Отправитель - ПланОбменаСсылка
//
Процедура ПриПолученииФайла(ЭлементДанных, ПолучениеЭлемента, Отправитель = Неопределено)
	
	ОбработатьПолученныеФайлы = Ложь;
	Если ПолучениеЭлемента = ПолучениеЭлементаДанных.Игнорировать Тогда
		
		// Стандартную обработку не переопределяем.
		
	ИначеЕсли ТипЗнч(ЭлементДанных) = Тип("СправочникОбъект.Файлы") Тогда
		
		Если ПолучениеФайлаЗапрещено(ЭлементДанных) Тогда
			ПолучениеЭлемента = ПолучениеЭлементаДанных.Игнорировать;
			Возврат;
		КонецЕсли;
		
		// Данные файла обрабатываем только в случае, если нет версий.
		// Если версии есть, то двоичные данные будут обработаны при обработке версий.
		ХранитьВерсии = ЭлементДанных.ХранитьВерсии;
		ОбработатьПолученныеФайлы = НЕ ХранитьВерсии;
		
	ИначеЕсли ТипЗнч(ЭлементДанных) = Тип("СправочникОбъект.ВерсииФайлов")
		Или (ЭтоЭлементРаботаСФайлами(ЭлементДанных)
			И ТипЗнч(ЭлементДанных) <> Тип("СправочникОбъект.ИдентификаторыОбъектовМетаданных")) Тогда
		
		// Справочник ИдентификаторыОбъектовМетаданных может пройти по результатам ЭтоЭлементРаботаСФайлами,
		// но не должен обрабатываться здесь.
		Если ПолучениеВерсииФайлаЗапрещено(ЭлементДанных) Тогда
			ПолучениеЭлемента = ПолучениеЭлементаДанных.Игнорировать;
			Возврат;
		КонецЕсли;
		ОбработатьПолученныеФайлы = Истина;
		
	КонецЕсли;
	
	Если ОбработатьПолученныеФайлы Тогда
		
		Если Отправитель <> Неопределено
			И ПланыОбмена.ИзменениеЗарегистрировано(Отправитель.Ссылка, ЭлементДанных) Тогда
			
			// Коллизия объекта (изменения зарегистрированы и на главном узле и на подчиненном).
			ПолучениеЭлемента = ПолучениеЭлементаДанных.Игнорировать;
			Возврат;
		КонецЕсли;
		
		Если ЭлементДанных.ЭтоГруппа Тогда
			Возврат;
		КонецЕсли;
		
		ДвоичныеДанные = ЭлементДанных.ФайлХранилище.Получить();
		НовыйТипХраненияФайла = ТипХраненияФайла(ЭлементДанных.Размер, ЭлементДанных.Расширение);
		ЭлементДанных.ТипХраненияФайла = НовыйТипХраненияФайла;
		
		Если Не ЭлементДанных.ЭтоНовый() Тогда
			ТипХраненияПредыдущейВерсииФайла = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(
													ЭлементДанных.Ссылка,
													"ТипХраненияФайла");
													
			Если ТипХраненияПредыдущейВерсииФайла = Перечисления.ТипыХраненияФайлов.ВТомахНаДиске Тогда
				СвойстваФайла = РаботаСФайламиВТомахСлужебный.СвойстваФайлаВТоме(ЭлементДанных.Ссылка);
				
				Если ЗначениеЗаполнено(СвойстваФайла.Том) Тогда
					ПутьДоФайла = РаботаСФайламиВТомахСлужебный.ПолноеИмяФайлаВТоме(СвойстваФайла);
					РаботаСФайламиВТомахСлужебный.УдалитьФайл(ПутьДоФайла);
				
					ЭлементДанных.Том = Справочники.ТомаХраненияФайлов.ПустаяСсылка();
					ЭлементДанных.ПутьКФайлу = "";
				КонецЕсли;
			Иначе
				УдалитьЗаписьИзРегистраДвоичныеДанныеФайлов(ЭлементДанных.Ссылка);
			КонецЕсли;
		КонецЕсли;
		
		Если НовыйТипХраненияФайла = Перечисления.ТипыХраненияФайлов.ВТомахНаДиске Тогда
			
			// По обмену пришел элемент с хранением в базе - но в базе приемнике принято хранить в томах.
			// Из служебного реквизита файл размещаем на томе и меняем ТипХраненияФайла на ВТомахНаДиске.
			ТипМетаданных = Метаданные.НайтиПоТипу(ТипЗнч(ЭлементДанных));
			
			Если ДвоичныеДанные = Неопределено Тогда
				
				ЭлементДанных.Том = Неопределено;
				ЭлементДанных.ПутьКФайлу = Неопределено;
				
				ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
							НСтр("ru = 'Не удалось добавить файл ни в один из томов, т.к. он не существует.
								|Возможно, файл удален антивирусной программой.
								|%1.'"),
								ОбщегоНазначенияКлиентСервер.ПолучитьИмяСРасширением(ЭлементДанных.Наименование, ЭлементДанных.Расширение));
				
				ЗаписьЖурналаРегистрации(НСтр("ru = 'Файлы.Добавление файла в том'", ОбщегоНазначения.КодОсновногоЯзыка()),
					УровеньЖурналаРегистрации.Ошибка, ТипМетаданных, ЭлементДанных.Ссылка, ТекстОшибки);
				
			Иначе
				РаботаСФайламиВТомахСлужебный.ДобавитьФайл(ЭлементДанных, ДвоичныеДанные, , Истина);
			КонецЕсли;
			
		Иначе
			
			Если ТипЗнч(ДвоичныеДанные) = Тип("ДвоичныеДанные") Тогда
				ЭлементДанных.ДополнительныеСвойства.Вставить("ДвоичныеДанныеФайла", ДвоичныеДанные);
			КонецЕсли;
			
			ЭлементДанных.ФайлХранилище = Новый ХранилищеЗначения(Неопределено);
			ЭлементДанных.Том = Справочники.ТомаХраненияФайлов.ПустаяСсылка();
			ЭлементДанных.ПутьКФайлу = "";
			
		КонецЕсли;
		
	КонецЕсли;
	
КонецПроцедуры


// Записывает двоичные данные файла в информационную базу.
//
// Параметры:
//  ПрисоединенныйФайл - ОпределяемыйТип.ПрисоединенныйФайл - ссылка на присоединенный файл.
//  ДвоичныеДанные     - ДвоичныеДанные - которые требуется записать.
//
Процедура ЗаписатьФайлВИнформационнуюБазу(Знач ПрисоединенныйФайл, Знач ДвоичныеДанные) Экспорт
	
	УстановитьОтключениеБезопасногоРежима(Истина);
	УстановитьПривилегированныйРежим(Истина);
	
	МенеджерЗаписи                     = РегистрыСведений.ДвоичныеДанныеФайлов.СоздатьМенеджерЗаписи();
	МенеджерЗаписи.Файл                = ПрисоединенныйФайл;
	МенеджерЗаписи.ДвоичныеДанныеФайла = Новый ХранилищеЗначения(ДвоичныеДанные, Новый СжатиеДанных(9));
	МенеджерЗаписи.Записать(Истина);
	
КонецПроцедуры

// Возвращает новый идентификатор объекта.
//  Для получения нового идентификатора выбирает последний идентификатор объекта
// из регистра НаличиеПрисоединенныхФайлов увеличивает его значение
// на одну единицу и возвращает полученный результат.
//
// Возвращаемое значение:
//  Строка - строка (10) - новый идентификатор объекта.
//
Функция ПолучитьОчереднойИдентификаторОбъекта() Экспорт
	
	// Вычисление нового идентификатора объекта.
	Результат = "0000000000"; // По длине ресурса ИдентификаторОбъекта.
	
	ТекстЗапроса =
	"ВЫБРАТЬ ПЕРВЫЕ 1
	|	НаличиеФайлов.ИдентификаторОбъекта КАК ИдентификаторОбъекта
	|ИЗ
	|	РегистрСведений.НаличиеФайлов КАК НаличиеФайлов
	|
	|УПОРЯДОЧИТЬ ПО
	|	ИдентификаторОбъекта УБЫВ";
	
	Запрос = Новый Запрос;
	Запрос.Текст = ТекстЗапроса;
	
	Выборка = Запрос.Выполнить().Выбрать();
	Если Выборка.Следующий() Тогда
		Идентификатор = Выборка.ИдентификаторОбъекта;
		
		Если ПустаяСтрока(Идентификатор) Тогда
			Возврат Результат;
		КонецЕсли;
		
		// Правила вычисления, как в обычном сложении: при 
		// заполнении текущего разряда следующий разряд увеличивается
		// на единицу, при этом, в текущем разряде значение становится
		// равным нулю. Значениями разрядов выступают символы
		// [0..9] и [a..z]. Таким образом один разряд может содержать
		// 36 значений.
		
		Позиция = 10; // 9- индекс 10-го символа
		Пока Позиция > 0 Цикл
			
			Символ = Сред(Идентификатор, Позиция, 1);
			
			Если Символ = "z" Тогда
				Идентификатор = Лев(Идентификатор, Позиция-1) + "0" + Прав(Идентификатор, 10 - Позиция);
				Позиция = Позиция - 1;
				Продолжить;
				
			ИначеЕсли Символ = "9" Тогда
				НовыйСимвол = "a";
			Иначе
				НовыйСимвол = Символ(КодСимвола(Символ)+1);
			КонецЕсли;
			
			Идентификатор = Лев(Идентификатор, Позиция-1) + НовыйСимвол + Прав(Идентификатор, 10 - Позиция);
			Прервать;
		КонецЦикла;
		
		Результат = Идентификатор;
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции

// См. РаботаСФайламиСлужебныйВМоделиСервиса.ОбновитьСостояниеОчередиИзвлеченияТекста.
Процедура ОбновитьСостояниеОчередиИзвлеченияТекста(ИсточникТекста, СостояниеИзвлеченияТекста) Экспорт
	
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.РаботаВМоделиСервиса.РаботаСФайламиВМоделиСервиса")
		И ОбщегоНазначения.РазделениеВключено()
		И ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
			
		МодульРаботаСФайламиСлужебныйВМоделиСервиса = ОбщегоНазначения.ОбщийМодуль("РаботаСФайламиСлужебныйВМоделиСервиса");
		МодульРаботаСФайламиСлужебныйВМоделиСервиса.ОбновитьСостояниеОчередиИзвлеченияТекста(ИсточникТекста, СостояниеИзвлеченияТекста);
		
	КонецЕсли;
	
КонецПроцедуры

// Сохраняет в регистре сведений рабочий каталог папки.
// Параметры:
//  СсылкаПапки  - СправочникСсылка.ПапкиФайлов - владелец файла.
//  РабочийКаталогВладельца - Строка - рабочий каталог папки.
//
Процедура СохранитьРабочийКаталогПапки(СсылкаПапки, РабочийКаталогПапки) Экспорт
	
	УстановитьПривилегированныйРежим(Истина);
	
	НаборЗаписей = РегистрыСведений.РабочиеКаталогиФайлов.СоздатьНаборЗаписей();
	
	НаборЗаписей.Отбор.Папка.Установить(СсылкаПапки);
	НаборЗаписей.Отбор.Пользователь.Установить(Пользователи.АвторизованныйПользователь());
	
	НоваяЗапись = НаборЗаписей.Добавить();
	НоваяЗапись.Папка = СсылкаПапки;
	НоваяЗапись.Пользователь = Пользователи.АвторизованныйПользователь();
	НоваяЗапись.Путь = РабочийКаталогПапки;
	
	НаборЗаписей.Записать();
	
КонецПроцедуры

// Очищает в регистре сведений рабочий каталог папки.
// Параметры:
//  СсылкаПапки  - СправочникСсылка.ПапкиФайлов - владелец файла.
//
Процедура ОчиститьРабочийКаталог(СсылкаПапки) Экспорт
	
	УстановитьПривилегированныйРежим(Истина);
	
	ТекущийПользователь = Пользователи.АвторизованныйПользователь();
	
	Блокировка = Новый БлокировкаДанных;
	
	ЭлементБлокировки = Блокировка.Добавить("Справочник.ПапкиФайлов");
	ЭлементБлокировки.УстановитьЗначение("Родитель", СсылкаПапки);
	ЭлементБлокировки.Режим = РежимБлокировкиДанных.Разделяемый;
	
	ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.РабочиеКаталогиФайлов");
	ЭлементБлокировки.УстановитьЗначение("Пользователь", ТекущийПользователь);
	
	НачатьТранзакцию();
	Попытка
		
		Блокировка.Заблокировать();
		
		НаборЗаписей = РегистрыСведений.РабочиеКаталогиФайлов.СоздатьНаборЗаписей();
		НаборЗаписей.Отбор.Папка.Установить(СсылкаПапки);
		НаборЗаписей.Отбор.Пользователь.Установить(ТекущийПользователь);
		НаборЗаписей.Записать(); // удаляем записи
		
		// Для дочерних папок очищаем рабочие каталоги.
		Запрос = Новый Запрос;
		Запрос.Текст =
		"ВЫБРАТЬ
		|	ПапкиФайлов.Ссылка КАК Ссылка
		|ИЗ
		|	Справочник.ПапкиФайлов КАК ПапкиФайлов
		|ГДЕ
		|	ПапкиФайлов.Родитель = &Ссылка";
		
		Запрос.УстановитьПараметр("Ссылка", СсылкаПапки);
		
		Результат = Запрос.Выполнить();
		Выборка = Результат.Выбрать();
		Пока Выборка.Следующий() Цикл
			ОчиститьРабочийКаталог(Выборка.Ссылка); // @skip-check query-in-loop - Рекурсивное удаление записей о файлах.
		КонецЦикла;
		
		ЗафиксироватьТранзакцию();
		
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры


#Область ДругиеПроцедурыИФункции

// Возвращает имя справочника для указанного владельца или вызывает исключение,
// если их более одного.
// 
// Параметры:
//  ВладелецФайлов  - ЛюбаяСсылка - объект, к которому добавляется файл.
//  ИмяСправочника  - Строка - если заполнено, то выполняется проверка
//                    наличия справочника среди справочников владельца для хранения файлов.
//                    Если не заполнено, возвращает имя основного справочника.
//  ЗаголовокОшибки - Строка - заголовок ошибки.
//                  - Неопределено - не вызывать исключение, а вернуть пустую строку.
//  ИмяПараметра    - Строка - имя требуемого параметра для определения имени справочника.
//  ОкончаниеОшибки - Строка - окончание ошибки (только для случая, когда ИмяПараметра = Неопределено).
// 
// Возвращаемое значение:
//  Строка - имя справочника
//
Функция ИмяСправочникаХраненияФайлов(ВладелецФайлов, ИмяСправочника = "",
	ЗаголовокОшибки = Неопределено, ОкончаниеОшибки = Неопределено) Экспорт
	
	НеВызыватьИсключение = (ЗаголовокОшибки = Неопределено);
	ИменаСправочников = ИменаСправочниковХраненияФайлов(ВладелецФайлов, НеВызыватьИсключение);
	
	Если ИменаСправочников.Количество() = 0 Тогда
		Если НеВызыватьИсключение Тогда
			Возврат "";
		КонецЕсли;
		
		ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			ЗаголовокОшибки + Символы.ПС
			+ НСтр("ru = 'У владельца файлов ""%1"" типа ""%2""
			             |нет справочников для хранения файлов.'"),
			Строка(ВладелецФайлов),
			Строка(ТипЗнч(ВладелецФайлов)));
	КонецЕсли;
	
	Если ЗначениеЗаполнено(ИмяСправочника) Тогда
		Если ИменаСправочников[ИмяСправочника] <> Неопределено Тогда
			Возврат ИмяСправочника;
		КонецЕсли;
	
		Если НеВызыватьИсключение Тогда
			Возврат "";
		КонецЕсли;
		
		ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			ЗаголовокОшибки + Символы.ПС
			+ НСтр("ru = 'У владельца файлов ""%1"" типа ""%2""
			             |нет справочника ""%3"" для хранения файлов.'"),
			Строка(ВладелецФайлов),
			Строка(ТипЗнч(ВладелецФайлов)),
			Строка(ИмяСправочника));
	КонецЕсли;
	
	ОсновнойСправочник = "";
	Для каждого КлючИЗначение Из ИменаСправочников Цикл
		Если КлючИЗначение.Значение = Истина Тогда
			ОсновнойСправочник = КлючИЗначение.Ключ;
			Прервать;
		КонецЕсли;
	КонецЦикла;
	
	Если ЗначениеЗаполнено(ОсновнойСправочник) Тогда
		Возврат ОсновнойСправочник;
	КонецЕсли;
		
	Если НеВызыватьИсключение Тогда
		Возврат "";
	КонецЕсли;
	
	ШаблонПричиныОшибки = 
		НСтр("ru = 'У владельца файлов ""%1"" типа ""%2""
			|не указан основной справочник для хранения файлов.'") + Символы.ПС;
			
	ПричинаОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		ШаблонПричиныОшибки, Строка(ВладелецФайлов), Строка(ТипЗнч(ВладелецФайлов)));
		
	ТекстОшибки = ЗаголовокОшибки + Символы.ПС
		+ ПричинаОшибки + Символы.ПС
		+ ОкончаниеОшибки;
		
	ВызватьИсключение СокрЛП(ТекстОшибки);
	
КонецФункции

// Возвращает соответствие имен справочников и значения Булево
// для указанного владельца.
// 
// Параметры:
//  ВладелецФайлов - ЛюбаяСсылка - объект, к которому добавляется файл.
// 
// Возвращаемое значение:
//  Соответствие из КлючИЗначение:
//   * Ключ - Строка
//   * Значение - Булево
//
Функция ИменаСправочниковХраненияВерсийФайлов(ВладелецФайлов, НеВызыватьИсключение = Ложь)
	
	Если ТипЗнч(ВладелецФайлов) = Тип("Тип") Тогда
		ТипВладельцаФайлов = ВладелецФайлов;
	Иначе
		ТипВладельцаФайлов = ТипЗнч(ВладелецФайлов);
	КонецЕсли;
	
	МетаданныеВладельца = Метаданные.НайтиПоТипу(ТипВладельцаФайлов);
	
	ИменаСправочников = Новый Соответствие;
	
	Если Метаданные.ОпределяемыеТипы.ВладелецФайлов.Тип.СодержитТип(ТипВладельцаФайлов) Тогда
		ИменаСправочников.Вставить("ВерсииФайлов", Истина);
	КонецЕсли;
	
	Возврат ИменаСправочников;
	
КонецФункции

// Возвращает имя справочника для указанного владельца или вызывает исключение,
// если их более одного.
// 
// Параметры:
//  ВладелецФайлов  - ЛюбаяСсылка - объект, к которому добавляется файл.
//  ИмяСправочника  - Строка - если заполнено, то выполняется проверка
//                    наличия справочника среди справочников владельца для хранения файлов.
//                    Если не заполнено, возвращает имя основного справочника.
//  ЗаголовокОшибки - Строка - заголовок ошибки.
//                  - Неопределено - не вызывать исключение, а вернуть пустую строку.
//  ИмяПараметра    - Строка - имя требуемого параметра для определения имени справочника.
//  ОкончаниеОшибки - Строка - окончание ошибки (только для случая, когда ИмяПараметра = Неопределено).
// 
// Возвращаемое значение:
//  Строка - имя справочника
//
Функция ИмяСправочникаХраненияВерсийФайлов(ВладелецФайлов, ИмяСправочника = "",
	ЗаголовокОшибки = Неопределено, ОкончаниеОшибки = Неопределено) Экспорт
	
	НеВызыватьИсключение = (ЗаголовокОшибки = Неопределено);
	ИменаСправочников = ИменаСправочниковХраненияВерсийФайлов(ВладелецФайлов, НеВызыватьИсключение);
	
	Если ИменаСправочников.Количество() = 0 Тогда
		Возврат "";
	КонецЕсли;
	
	ОсновнойСправочник = "";
	Для каждого КлючИЗначение Из ИменаСправочников Цикл
		Если КлючИЗначение.Значение = Истина Тогда
			ОсновнойСправочник = КлючИЗначение.Ключ;
			Прервать;
		КонецЕсли;
	КонецЦикла;
	
	Если ЗначениеЗаполнено(ОсновнойСправочник) Тогда
		Возврат ОсновнойСправочник;
	КонецЕсли;
		
	Если НеВызыватьИсключение Тогда
		Возврат "";
	КонецЕсли;
	
	ШаблонПричиныОшибки = 
		НСтр("ru = 'У владельца версий файлов ""%1""
			|не указан основной справочник для хранения версий файлов.'") + Символы.ПС;
			
	ПричинаОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		ШаблонПричиныОшибки, Строка(ВладелецФайлов));
		
	ТекстОшибки = ЗаголовокОшибки + Символы.ПС
		+ ПричинаОшибки + Символы.ПС
		+ ОкончаниеОшибки;
		
	ВызватьИсключение СокрЛП(ТекстОшибки);
	
КонецФункции

// Отменяет редактирование файла.
//
// Параметры:
//  ПрисоединенныйФайл - ОпределяемыйТип.ПрисоединенныйФайл
//                     - ОпределяемыйТип.ПрисоединенныйФайлОбъект - ссылка или 
//                     объект присоединенного файла, который требуется освободить.
//
Процедура ОсвободитьФайл(Знач ПрисоединенныйФайл) Экспорт
	
	НачатьТранзакцию();
	Попытка
	
		Если Справочники.ТипВсеСсылки().СодержитТип(ТипЗнч(ПрисоединенныйФайл)) Тогда
			БлокировкаДанных              = Новый БлокировкаДанных;
			ЭлементБлокировкиДанных       = БлокировкаДанных.Добавить(Метаданные.НайтиПоТипу(ТипЗнч(ПрисоединенныйФайл)).ПолноеИмя());
			ЭлементБлокировкиДанных.УстановитьЗначение("Ссылка", ПрисоединенныйФайл);
			БлокировкаДанных.Заблокировать();
			ФайлОбъект = ПрисоединенныйФайл.ПолучитьОбъект();
		Иначе
			ФайлОбъект = ПрисоединенныйФайл;
		КонецЕсли;
		
		Если ЗначениеЗаполнено(ФайлОбъект.Редактирует) Тогда
			ФайлОбъект.Редактирует = Справочники.Пользователи.ПустаяСсылка();
			ФайлОбъект.Записать();
		КонецЕсли;
		
		ЗафиксироватьТранзакцию();
		
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

Функция КоличествоЗанятыхФайлов(Знач ВладелецФайла = Неопределено, Знач Редактирует = Неопределено) Экспорт
	
	УстановитьПривилегированныйРежим(Истина);
	
	Запрос = Новый Запрос;
	Запрос.Текст = 
		"ВЫБРАТЬ КОЛИЧЕСТВО(1) КАК Количество
		|ИЗ
		|	РегистрСведений.СведенияОФайлах КАК СведенияОФайлах
		|ГДЕ
		|	СведенияОФайлах.Редактирует <> ЗНАЧЕНИЕ(Справочник.Пользователи.ПустаяСсылка)";
	
	Если Редактирует = Неопределено Тогда 
		Редактирует = Пользователи.АвторизованныйПользователь();
	КонецЕсли;
		
	Запрос.Текст = Запрос.Текст + " И СведенияОФайлах.Редактирует = &Редактирует ";
	Запрос.УстановитьПараметр("Редактирует", Редактирует);
	
	Если ВладелецФайла <> Неопределено Тогда 
		Запрос.Текст = Запрос.Текст + " И СведенияОФайлах.ВладелецФайла = &ВладелецФайла ";
		Запрос.УстановитьПараметр("ВладелецФайла", ВладелецФайла);
	КонецЕсли;
	
	Выборка = Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Количество");
	Возврат Выборка[0];
	
КонецФункции

// Сравнивает 2 элемента отбора компоновки данных.
// Параметры:
//   Элемент1 - ЭлементОтбораКомпоновкиДанных
//            - ГруппаЭлементовОтбораКомпоновкиДанных - элемент условного оформления списка.
//   Элемент2 - ЭлементОтбораКомпоновкиДанных
//            - ГруппаЭлементовОтбораКомпоновкиДанных - элемент условного оформления списка.
//
// Возвращаемое значение:
//   Булево - результат сравнения.
//
Функция СравнитьЭлементыОтбора(Элемент1, Элемент2)
	
	Если Элемент1.Использование = Элемент2.Использование
		И ТипЗнч(Элемент1) = ТипЗнч(Элемент2) Тогда
		
		Если ТипЗнч(Элемент1) = Тип("ЭлементОтбораКомпоновкиДанных") Тогда
			Если Элемент1.ВидСравнения <> Элемент2.ВидСравнения
				ИЛИ Элемент1.ЛевоеЗначение <> Элемент2.ЛевоеЗначение
				ИЛИ Элемент1.ПравоеЗначение <> Элемент2.ПравоеЗначение Тогда
				Возврат Ложь;
			КонецЕсли;
		Иначе
			
			ЧислоЭлементов = Элемент1.Элементы.Количество();
			Если Элемент1.ТипГруппы <> Элемент2.ТипГруппы
				ИЛИ ЧислоЭлементов <> Элемент2.Элементы.Количество() Тогда
				Возврат Ложь;
			КонецЕсли;
			
			Для Индекс = 0 По ЧислоЭлементов - 1 Цикл
				ПодчиненныйЭлемент1 = Элемент1.Элементы[Индекс];
				ПодчиненныйЭлемент2 = Элемент2.Элементы[Индекс];
				ЭлементыРавны = СравнитьЭлементыОтбора(ПодчиненныйЭлемент1, ПодчиненныйЭлемент2);
				
				Если Не ЭлементыРавны Тогда
					Возврат ЭлементыРавны;
				КонецЕсли;
			КонецЦикла;
			
		КонецЕсли;
	Иначе
		Возврат Ложь;
	КонецЕсли;
	
	Возврат Истина;
	
КонецФункции

// Формирует отчет для файлов с ошибками.
//
// Параметры:
//   МассивИменФайловСОшибками - Массив из Строка - пути к файлам.
//
// Возвращаемое значение:
//  ТабличныйДокумент
//
Функция ИмпортФайловСформироватьОтчет(МассивИменФайловСОшибками) Экспорт
	
	Документ = Новый ТабличныйДокумент;
	Макет = Справочники.Файлы.ПолучитьМакет("МакетОтчета");
	
	ОбластьЗаголовок = Макет.ПолучитьОбласть("Заголовок");
	ОбластьЗаголовок.Параметры.Описание = НСтр("ru = 'Не удалось загрузить следующие файлы:'");
	Документ.Вывести(ОбластьЗаголовок);
	
	ОбластьСтрока = Макет.ПолучитьОбласть("Строка");

	Для Каждого Выборка Из МассивИменФайловСОшибками Цикл
		ОбластьСтрока.Параметры.Название = Выборка.ИмяФайла;
		ОбластьСтрока.Параметры.Ошибка = Выборка.Ошибка;
		Документ.Вывести(ОбластьСтрока);
	КонецЦикла;
	
	Отчет = Новый ТабличныйДокумент;
	Отчет.Вывести(Документ);

	Возврат Отчет;
	
КонецФункции

// Заполняет условное оформление списка файлов.
//
// Параметры:
//   Список - ДинамическийСписок
//
Процедура ЗаполнитьУсловноеОформлениеСпискаФайлов(Список) Экспорт
	
	УсловноеОформлениеКД = Список.КомпоновщикНастроек.Настройки.УсловноеОформление;
	УсловноеОформлениеКД.ИдентификаторПользовательскойНастройки = "ОсновноеОформление";
	
	Элемент = УсловноеОформлениеКД.Элементы.Добавить();
	Элемент.Использование = Истина;
	Элемент.Оформление.УстановитьЗначениеПараметра("ЦветТекста", ЦветаСтиля.ТекстЗапрещеннойЯчейкиЦвет);
	
	ГруппаОтбора = Элемент.Отбор.Элементы.Добавить(Тип("ГруппаЭлементовОтбораКомпоновкиДанных"));
	
	Отбор = ГруппаОтбора.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
	Отбор.Использование = Истина;
	Отбор.ВидСравнения = ВидСравненияКомпоновкиДанных.Заполнено;
	Отбор.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Редактирует");

	Отбор = ГруппаОтбора.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
	Отбор.Использование = Истина;
	Отбор.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
	Отбор.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Служебный");
	Отбор.ПравоеЗначение = Ложь;
	
	Если ЕстьДублирующийсяЭлемент(УсловноеОформлениеКД.Элементы, Элемент) Тогда
		УсловноеОформлениеКД.Элементы.Удалить(Элемент);
	КонецЕсли;
	
	Элемент = УсловноеОформлениеКД.Элементы.Добавить();
	Элемент.Использование = Истина;
	Элемент.Оформление.УстановитьЗначениеПараметра("ЦветТекста", ЦветаСтиля.ФайлЗанятыйТекущимПользователем);
	
	ГруппаОтбора = Элемент.Отбор.Элементы.Добавить(Тип("ГруппаЭлементовОтбораКомпоновкиДанных"));
	
	Отбор = Элемент.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
	Отбор.Использование = Истина;
	Отбор.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
	Отбор.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Редактирует");
	Отбор.ПравоеЗначение = Пользователи.АвторизованныйПользователь();
	
	Отбор = ГруппаОтбора.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
	Отбор.Использование = Истина;
	Отбор.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
	Отбор.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Служебный");
	Отбор.ПравоеЗначение = Ложь;
	
	Если ЕстьДублирующийсяЭлемент(УсловноеОформлениеКД.Элементы, Элемент) Тогда
		УсловноеОформлениеКД.Элементы.Удалить(Элемент);
	КонецЕсли;
	
	Элемент = УсловноеОформлениеКД.Элементы.Добавить();
	Элемент.Использование = Истина;
	Элемент.Оформление.УстановитьЗначениеПараметра("ЦветТекста", ЦветаСтиля.ТекстЗапрещеннойЯчейкиЦвет);
	
	Отбор = Элемент.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
	Отбор.Использование = Истина;
	Отбор.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
	Отбор.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Служебный");
	Отбор.ПравоеЗначение = Истина;
	
	Если ЕстьДублирующийсяЭлемент(УсловноеОформлениеКД.Элементы, Элемент) Тогда
		УсловноеОформлениеКД.Элементы.Удалить(Элемент);
	КонецЕсли;
	
КонецПроцедуры

// Заполняет условное оформление списка папок.
//
// Параметры:
//   Папки - ДинамическийСписок
//
Процедура ЗаполнитьУсловноеОформлениеСпискаПапок(Папки) Экспорт
	
	УсловноеОформлениеКД = Папки.КомпоновщикНастроек.Настройки.УсловноеОформление;
	УсловноеОформлениеКД.ИдентификаторПользовательскойНастройки = "ОсновноеОформление";
	
	Элемент = УсловноеОформлениеКД.Элементы.Добавить();
	Элемент.Использование = Истина;
	Элемент.Оформление.УстановитьЗначениеПараметра("ЦветТекста", ЦветаСтиля.ТекстЗапрещеннойЯчейкиЦвет);
	
	Отбор = Элемент.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
	Отбор.Использование = Истина;
	Отбор.ВидСравнения = ВидСравненияКомпоновкиДанных.Заполнено;
	
	Отбор.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
	Отбор.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("ПапкаСинхронизируется");
	Отбор.ПравоеЗначение = Истина;
	
	Если ЕстьДублирующийсяЭлемент(УсловноеОформлениеКД.Элементы, Элемент) Тогда
		УсловноеОформлениеКД.Элементы.Удалить(Элемент);
	КонецЕсли;
	
КонецПроцедуры

// Есть ли дублирующийся элемент в условном оформлении списка.
// Параметры:
//   Элементы - Массив из ЭлементУсловногоОформления - массив элементов условного оформления списка.
//   ЭлементПоиска - ЭлементУсловногоОформления - элемент условного оформления списка.
//
// Возвращаемое значение:
//   Булево - есть дублирующийся элемент.
//
Функция ЕстьДублирующийсяЭлемент(Элементы, ЭлементПоиска)
	
	Для Каждого Элемент Из Элементы Цикл
		Если Элемент <> ЭлементПоиска Тогда
			
			Если Элемент.Оформление.Элементы.Количество() <> ЭлементПоиска.Оформление.Элементы.Количество() Тогда
				Продолжить;
			КонецЕсли;
			
			НайденОтличающийсяЭлемент = Ложь;
			
			// Обходим все элементы оформления - если есть хоть один отличающийся - делаем Продолжить;
			ЧислоЭлементов = Элемент.Оформление.Элементы.Количество();
			Для Индекс = 0 По ЧислоЭлементов - 1 Цикл
				Элемент1 = Элемент.Оформление.Элементы[Индекс]; // ЗначениеПараметраНастроекКомпоновкиДанных
				Элемент2 = ЭлементПоиска.Оформление.Элементы[Индекс]; // ЗначениеПараметраНастроекКомпоновкиДанных
				
				Если Элемент1.Использование И Элемент2.Использование Тогда
					Если Элемент1.Параметр <> Элемент2.Параметр ИЛИ Элемент1.Значение <> Элемент2.Значение Тогда
						НайденОтличающийсяЭлемент = Истина;
						Прервать;
					КонецЕсли;
				КонецЕсли;
			КонецЦикла;
			
			Если НайденОтличающийсяЭлемент Тогда
				Продолжить;
			КонецЕсли;
			
			Если Элемент.Отбор.Элементы.Количество() <> ЭлементПоиска.Отбор.Элементы.Количество() Тогда
				Продолжить;
			КонецЕсли;
			
			// Обходим все элементы отбора - если есть хоть один отличающийся - делаем Продолжить;
			ЧислоЭлементов = Элемент.Отбор.Элементы.Количество();
			Для Индекс = 0 По ЧислоЭлементов - 1 Цикл
				Элемент1 = Элемент.Отбор.Элементы[Индекс];
				Элемент2 = ЭлементПоиска.Отбор.Элементы[Индекс];
				
				ЭлементыРавны = СравнитьЭлементыОтбора(Элемент1, Элемент2);
				Если Не ЭлементыРавны Тогда
					НайденОтличающийсяЭлемент = Истина;
					Прервать;
				КонецЕсли;
				
			КонецЦикла;
			
			Если НайденОтличающийсяЭлемент Тогда
				Продолжить;
			КонецЕсли;
			
			// Обошли все элементы оформления и отбора - они все одинаковы - это дубль.
			Возврат Истина;
			
		КонецЕсли;
	КонецЦикла;
	
	Возврат Ложь;
	
КонецФункции

// Делает ПоместитьВоВременноеХранилище (если файл хранится в томе) и возвращает нужную ссылку.
//
// Параметры:
//  ВерсияСсылка - СправочникСсылка.ВерсииФайлов - версия файла.
//  ИдентификаторФормы - УникальныйИдентификатор
//                     - Неопределено - уникальный идентификатор формы.
//  ВыбрасыватьИсключение - Булево - выбрасывать исключение, если тип хранения "В томах"
//									 и во время получения файла возникла ошибка.
//
// Возвращаемое значение:
//   Строка - навигационная ссылка.
//
Функция ПолучитьНавигационнуюСсылкуВоВременномХранилище(ВерсияСсылка, ИдентификаторФормы = Неопределено, ВыбрасыватьИсключение = Истина) Экспорт
	
	Адрес = "";
	ТипХраненияФайла = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(ВерсияСсылка, "ТипХраненияФайла");
	Если ТипХраненияФайла = Перечисления.ТипыХраненияФайлов.ВТомахНаДиске Тогда
		ДвоичныеДанные = РаботаСФайламиВТомахСлужебный.ДанныеФайла(ВерсияСсылка, ВыбрасыватьИсключение);
		Адрес = ПоместитьВоВременноеХранилище(ДвоичныеДанные, ИдентификаторФормы);
	Иначе
		ДвоичныеДанные = Неопределено;
		ХранилищеФайла = РаботаСФайлами.ХранилищеФайлаИзИнформационнойБазы(ВерсияСсылка);
		// Хранилище = Неопределено, если данные очищены.
		Если ХранилищеФайла <> Неопределено Тогда
			ДвоичныеДанные = ХранилищеФайла.Получить();
		КонецЕсли;
		
		Адрес = ПоместитьВоВременноеХранилище(ДвоичныеДанные, ИдентификаторФормы);
	КонецЕсли;
	
	Возврат Адрес;
	
КонецФункции

// Получает значение настройки  ПоказыватьКолонкуРазмер.
// Возвращаемое значение:
//   Булево - показывать колонку размер.
//
Функция ПолучитьПоказыватьКолонкуРазмер() Экспорт
	
	ПоказыватьКолонкуРазмер = ОбщегоНазначения.ХранилищеОбщихНастроекЗагрузить("НастройкиПрограммы", "ПоказыватьКолонкуРазмер");
	Если ПоказыватьКолонкуРазмер = Неопределено Тогда
		ПоказыватьКолонкуРазмер = Ложь;
		ОбщегоНазначения.ХранилищеОбщихНастроекСохранить("НастройкиПрограммы", "ПоказыватьКолонкуРазмер", ПоказыватьКолонкуРазмер);
	КонецЕсли;
	
	Возврат ПоказыватьКолонкуРазмер;
	
КонецФункции

// Возвращаемое значение:
//   Строка
//
Функция ОшибкаФайлНеНайденВХранилищеФайлов(ФайлОбъект)
	
	ИмяФайла = ОбщегоНазначенияКлиентСервер.ПолучитьИмяСРасширением(ФайлОбъект.Наименование, ФайлОбъект.Расширение);
	ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		НСтр("ru = 'Не удалось открыть файл:
			|%1
			|который присоединен к:
			|%2'"),
		ИмяФайла, ОбщегоНазначения.ПредметСтрокой(ФайлОбъект.ВладелецФайла));

	Если ФайлОбъект.ПометкаУдаления Тогда
		ТекстОшибки = ТекстОшибки + Символы.ПС + Символы.ПС
			+ НСтр("ru = 'Файл помечен на удаление и очищен как ненужный.'");	
	ИначеЕсли ФайлОбъект.ТипХраненияФайла = Перечисления.ТипыХраненияФайлов.ВТомахНаДиске Тогда
		ТекстОшибки = ТекстОшибки + Символы.ПС + Символы.ПС
			+ НСтр("ru = 'Данные файла недоступны, т.к. возможно файл был очищен как ненужный или удален антивирусной программой.'");
	Иначе
		ТекстОшибки = ТекстОшибки + Символы.ПС + Символы.ПС
			+ НСтр("ru = 'Данные файла недоступны, т.к. возможно файл был очищен как ненужный.'");
	КонецЕсли;
	
	Возврат ТекстОшибки;
	
КонецФункции

// Параметры:
//   ФайлОбъект - ОпределяемыйТип.ПрисоединенныйФайлОбъект
//   ВызыватьИсключение - Булево
//
Процедура СообщитьОбОшибкеФайлНеНайден(ФайлОбъект, ВызыватьИсключение) Экспорт
	
	СообщениеОбОшибке = ОшибкаФайлНеНайденВХранилищеФайлов(ФайлОбъект);
	КритичностьОшибки = ?(ВызыватьИсключение, УровеньЖурналаРегистрации.Ошибка, УровеньЖурналаРегистрации.Предупреждение);
	ЗаписьЖурналаРегистрации(НСтр("ru = 'Файлы.Открытие файла'", ОбщегоНазначения.КодОсновногоЯзыка()),
		КритичностьОшибки, ФайлОбъект.Ссылка.Метаданные(), ФайлОбъект.Ссылка, СообщениеОбОшибке);
		
	Если ВызыватьИсключение Тогда
		ВызватьИсключение СообщениеОбОшибке;
	КонецЕсли;
	
КонецПроцедуры	

// Возвращает номер по нарастанию. Предыдущее значение берется из регистра сведений НомераОтсканированныхФайлов.
// Параметры:
//   Владелец - ЛюбаяСсылка - владелец файла.
//
// Возвращаемое значение:
//   Число  - новый номер для сканирования.
//
Функция ПолучитьНовыйНомерДляСканирования(Владелец) Экспорт
	
	// Подготовить структуру отбора по измерениям.
	СтруктураОтбора = Новый Структура;
	СтруктураОтбора.Вставить("Владелец", Владелец);
	
	НачатьТранзакцию();
	Попытка
		
		Блокировка = Новый БлокировкаДанных;
		ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.НомераОтсканированныхФайлов");
		ЭлементБлокировки.УстановитьЗначение("Владелец", Владелец);
		Блокировка.Заблокировать();
	
		// Получить структуру с данными ресурсов записи.
		СтруктураРесурсов = РегистрыСведений.НомераОтсканированныхФайлов.Получить(СтруктураОтбора);
		
		// Получить максимальный номер из регистра.
		Номер = СтруктураРесурсов.Номер;
		Номер = Номер + 1; // увеличим на 1
		
		УстановитьОтключениеБезопасногоРежима(Истина);
		УстановитьПривилегированныйРежим(Истина);
		
		// Запишем новый номер в регистр.
		НаборЗаписей = РегистрыСведений.НомераОтсканированныхФайлов.СоздатьНаборЗаписей();
		
		НаборЗаписей.Отбор.Владелец.Установить(Владелец);
		
		НоваяЗапись = НаборЗаписей.Добавить();
		НоваяЗапись.Владелец = Владелец;
		НоваяЗапись.Номер = Номер;
		
		НаборЗаписей.Записать();
		
		УстановитьПривилегированныйРежим(Ложь);
		УстановитьОтключениеБезопасногоРежима(Ложь);
		
		ЗафиксироватьТранзакцию();
		
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
	Возврат Номер;
	
КонецФункции

// Определяет наличие опционального реквизита Служебный в метаданных справочника.
//
// Параметры:
//  ИмяСправочника - Строка - имя справочника в метаданных.
//
// Возвращаемое значение:
//  Булево - наличие реквизита Служебный.
//
Функция ЕстьРеквизитСлужебный(Знач ИмяСправочника) Экспорт
	
	ОбъектМетаданных  = Метаданные.Справочники[ИмяСправочника];
	РеквизитСлужебный = ОбъектМетаданных.Реквизиты.Найти("Служебный");
	Возврат РеквизитСлужебный <> Неопределено;
	
КонецФункции

// Параметры:
//  ФайлСсылка - СправочникСсылка.Файлы
// 
// Возвращаемое значение:
//  СправочникСсылка.ВерсииФайлов
//
Функция ТекущаяВерсия(ФайлСсылка)
	Возврат ОбщегоНазначения.ЗначениеРеквизитаОбъекта(ФайлСсылка, "ТекущаяВерсия");
КонецФункции

// Добавляет элементы отбора компоновки данных в динамические списки файлов.
//
// Параметры:
//  Список - ДинамическийСписок - динамический список, в который будут добавлены отборы.
//
Процедура ДобавитьОтборыВСписокФайлов(Список) Экспорт
	
	ОбщегоНазначенияКлиентСервер.ДобавитьЭлементКомпоновки(Список.Отбор, "Служебный", ВидСравненияКомпоновкиДанных.НеРавно, 
		Истина, "СкрыватьСлужебные", Истина);
	
	УстановитьОтборПоПометкеУдаления(Список.Отбор);
	
КонецПроцедуры

// Изменяет видимость элементов формы присоединенного файла для работы внешних пользователей.
// Внешним пользователям доступна только общая информация о файле и его характеристиках.
//
// Параметры:
//  Форма - ФормаКлиентскогоПриложения - форма, для которой изменяется видимость элементов.
//  ЭтоФормаСписка - Булево - признак того, что процедура вызывается из формы списка.
//
Процедура ИзменитьФормуДляВнешнегоПользователя(Форма, Знач ЭтоФормаСписка = Ложь) Экспорт
	
	Элементы = Форма.Элементы;
	Если ЭтоФормаСписка Тогда
		Элементы.СписокАвтор.Видимость = Ложь;
		Элементы.СписокРедактирует.Видимость = Ложь;
		Элементы.СписокНомерКартинкиПодписанЗашифрован.Видимость = Ложь;
		Если Элементы.Найти("СписокОтредактировал") <> Неопределено Тогда
			Элементы.СписокОтредактировал.Видимость = Ложь;
		КонецЕсли;
	Иначе
		Элементы.ГруппаХарактеристикиФайла.Видимость = Истина;
		Элементы.ГруппаДополнительныеДанныеСтраницы.Видимость = Ложь;
		Элементы.ФормаГруппаКомандЭлектроннаяПодписьИШифрование.Видимость = Ложь;
	КонецЕсли;
	
КонецПроцедуры

Процедура УстановитьОтборПоПометкеУдаления(Область) Экспорт
	
	ОбщегоНазначенияКлиентСервер.ДобавитьЭлементКомпоновки(Область, "ПометкаУдаления",
		ВидСравненияКомпоновкиДанных.Равно, Ложь, "СкрыватьПометкуУдаленияПоУмолчанию", Истина,
		РежимОтображенияЭлементаНастройкиКомпоновкиДанных.Недоступный);
	
КонецПроцедуры

#КонецОбласти

#Область ПользовательскиеНастройки

// Считаем ДействиеПоДвойномуЩелчкуМыши - если первый раз - поставим правильное значение.
//
// Возвращаемое значение:
//   Строка - действие по двойному щелчку мыши.
//
Функция ДействиеПоДвойномуЩелчкуМыши()
	
	КакОткрывать = ОбщегоНазначения.ХранилищеОбщихНастроекЗагрузить(
		"НастройкиОткрытияФайлов", "ДействиеПоДвойномуЩелчкуМыши");
	
	Если КакОткрывать = Неопределено
	 ИЛИ КакОткрывать = Перечисления.ДействияСФайламиПоДвойномуЩелчку.ПустаяСсылка() Тогда
		
		КакОткрывать = Перечисления.ДействияСФайламиПоДвойномуЩелчку.ОткрыватьФайл;
		
		ОбщегоНазначения.ХранилищеОбщихНастроекСохранить(
			"НастройкиОткрытияФайлов", "ДействиеПоДвойномуЩелчкуМыши", КакОткрывать);
	КонецЕсли;
	
	Если КакОткрывать = Перечисления.ДействияСФайламиПоДвойномуЩелчку.ОткрыватьФайл Тогда
		Возврат "ОткрыватьФайл";
	Иначе
		Возврат "ОткрыватьКарточку";
	КонецЕсли;
	
КонецФункции

// Считаем из настроек СпособСравненияВерсийФайлов.
//
// Возвращаемое значение:
//   Строка - способ сравнения версий файлов.
//
Функция СпособСравненияВерсийФайлов()
	
	СпособСравнения = ОбщегоНазначения.ХранилищеОбщихНастроекЗагрузить(
		"НастройкиСравненияФайлов", "СпособСравненияВерсийФайлов");
	
	Если СпособСравнения = Перечисления.СпособыСравненияВерсийФайлов.MicrosoftOfficeWord Тогда
		Возврат "MicrosoftOfficeWord";
		
	ИначеЕсли СпособСравнения = Перечисления.СпособыСравненияВерсийФайлов.OpenOfficeOrgWriter Тогда
		Возврат "OpenOfficeOrgWriter";
	Иначе
		Возврат Неопределено;
	КонецЕсли;
	
КонецФункции

// Возвращает настройку - Спрашивать режим редактирования при открытии файла.
// Возвращаемое значение:
//   Булево - спрашивать режим редактирования при открытии файла.
//
Функция СпрашиватьРежимРедактированияПриОткрытииФайла()
	СпрашиватьРежимРедактированияПриОткрытииФайла = 
		ОбщегоНазначения.ХранилищеОбщихНастроекЗагрузить("НастройкиОткрытияФайлов", "СпрашиватьРежимРедактированияПриОткрытииФайла");
	Если СпрашиватьРежимРедактированияПриОткрытииФайла = Неопределено Тогда
		СпрашиватьРежимРедактированияПриОткрытииФайла = Истина;
		ОбщегоНазначения.ХранилищеОбщихНастроекСохранить("НастройкиОткрытияФайлов", "СпрашиватьРежимРедактированияПриОткрытииФайла", СпрашиватьРежимРедактированияПриОткрытииФайла);
	КонецЕсли;
	
	Возврат СпрашиватьРежимРедактированияПриОткрытииФайла;
КонецФункции

// Возвращает настройку -  режим при открытии файла: просмотр или редактирование.
//
// Возвращаемое значение:
//   Строка - режим при открытии файла. Варианты: "Просмотр", "Редактирование".
//
Функция ВариантОткрытияФайла()
	
	ВариантОткрытияФайла = 
		ОбщегоНазначения.ХранилищеОбщихНастроекЗагрузить("НастройкиОткрытияФайлов", "ВариантОткрытияФайла");
	Если ВариантОткрытияФайла = Неопределено Тогда
		ВариантОткрытияФайла = "Открыть";
		ОбщегоНазначения.ХранилищеОбщихНастроекСохранить("НастройкиОткрытияФайлов", "ВариантОткрытияФайла", ВариантОткрытияФайла);
	КонецЕсли;
	
	Возврат ВариантОткрытияФайла;
КонецФункции

#КонецОбласти

#Область РаботаСКодировками

// Возвращает таблицу имен кодировок.
//
// Возвращаемое значение:
//   СписокЗначений:
//     * Значение - Строка - например "ibm852".
//     * Представление - Строка - например "ibm852 (Центральноевропейская DOS)".
//
Функция Кодировки() Экспорт
	
	Возврат РаботаСФайламиСлужебныйКлиентСервер.Кодировки();

КонецФункции

#КонецОбласти

#Область ВспомогательныеПроцедурыИФункции

// Отмечает файл, как редактируемый.
//
// Параметры:
//  ПрисоединенныйФайл - ссылка или Объект присоединенного файла, который требуется отметить.
//
Процедура ЗанятьФайлДляРедактированияСервер(Знач ПрисоединенныйФайл, Пользователь = Неопределено) Экспорт
	
	НачатьТранзакцию();
	Попытка
		Если Справочники.ТипВсеСсылки().СодержитТип(ТипЗнч(ПрисоединенныйФайл)) Тогда
			БлокировкаДанных = Новый БлокировкаДанных;
			ЭлементБлокировкиДанных = БлокировкаДанных.Добавить(Метаданные.НайтиПоТипу(ТипЗнч(ПрисоединенныйФайл)).ПолноеИмя());
			ЭлементБлокировкиДанных.УстановитьЗначение("Ссылка", ПрисоединенныйФайл);
			БлокировкаДанных.Заблокировать();
			ЗаблокироватьДанныеДляРедактирования(ПрисоединенныйФайл);
			
			ФайлОбъект = ПрисоединенныйФайл.ПолучитьОбъект();
		Иначе
			ФайлОбъект = ПрисоединенныйФайл;
		КонецЕсли;
		
		Если Пользователь = Неопределено Тогда
			ФайлОбъект.Редактирует = Пользователи.АвторизованныйПользователь();
		Иначе
			ФайлОбъект.Редактирует = Пользователь;
		КонецЕсли;
		ФайлОбъект.Записать();
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

Процедура ЗаписатьДанныеФайлаВРегистрПриОбмене(Источник)
	
	Перем ДвоичныеДанныеФайла;
	
	Если Источник.ДополнительныеСвойства.Свойство("ДвоичныеДанныеФайла", ДвоичныеДанныеФайла) Тогда
		НаборЗаписей = РегистрыСведений.ДвоичныеДанныеФайлов.СоздатьНаборЗаписей();
		НаборЗаписей.Отбор.Файл.Установить(Источник.Ссылка);
		
		Запись = НаборЗаписей.Добавить();
		Запись.Файл = Источник.Ссылка;
		Запись.ДвоичныеДанныеФайла = Новый ХранилищеЗначения(ДвоичныеДанныеФайла);
		
		НаборЗаписей.ОбменДанными.Загрузка = Истина;
		НаборЗаписей.Записать();
		
		Источник.ДополнительныеСвойства.Удалить("ДвоичныеДанныеФайла");
	КонецЕсли;
	
КонецПроцедуры

Функция ПолучениеФайлаЗапрещено(ЭлементДанных)
	
	Возврат ЭлементДанных.ЭтоНовый()
	      И Не ПроверитьРасширениеФайлаДляЗагрузки(ЭлементДанных.Расширение, Ложь);
	
КонецФункции

Функция ПолучениеВерсииФайлаЗапрещено(ЭлементДанных)
	
	Возврат ЭлементДанных.ЭтоНовый()
	      И Не ПроверитьРасширениеФайлаДляЗагрузки(ЭлементДанных.Расширение, Ложь);
	
КонецФункции

Процедура ОбработатьОтправкуФайлаПоТипуХранения(ЭлементДанных)
	
	Если ЭлементДанных.ЭтоГруппа Тогда
		Возврат;
	КонецЕсли;
	
	Если ЭлементДанных.ТипХраненияФайла = Перечисления.ТипыХраненияФайлов.ВТомахНаДиске Тогда
		
		// Помещаем данные файла из тома в служебный реквизит справочника.
		РаботаСФайламиВТомахСлужебный.ПоместитьФайлВРеквизитСправочника(ЭлементДанных);
		
	Иначе
		// Перечисления.ТипыХраненияФайлов.ВИнформационнойБазе
		// Если есть возможность хранения версий файлов - двоичные данные берутся из текущей версии.
		Если ЭлементДанных.Метаданные().Реквизиты.Найти("ТекущаяВерсия") <> Неопределено
			И ЗначениеЗаполнено(ЭлементДанных.ТекущаяВерсия) Тогда
			ИсточникДвоичныхДанных = ЭлементДанных.ТекущаяВерсия;
		Иначе
			ИсточникДвоичныхДанных = ЭлементДанных.Ссылка;
		КонецЕсли;
		Попытка
			// Помещаем данные файла из информационной базы в служебный реквизит справочника.
			АдресВоВременномХранилище = ПолучитьНавигационнуюСсылкуВоВременномХранилище(ИсточникДвоичныхДанных,,Ложь);
			ЭлементДанных.ФайлХранилище = Новый ХранилищеЗначения(ПолучитьИзВременногоХранилища(АдресВоВременномХранилище), Новый СжатиеДанных(9));
		Исключение
			// Возможно, файл не был найден. Отправку данных не прерываем.
			// АПК:154-выкл - отсутствие данных файла штатная ситуация.
			ЗаписьЖурналаРегистрации(СобытиеЖурналаРегистрацииДляОбмена(), 
				УровеньЖурналаРегистрации.Предупреждение,,, 
				ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
			// АПК:154-вкл
			ЭлементДанных.ФайлХранилище = Новый ХранилищеЗначения(Неопределено);
		КонецПопытки;
		
		ЭлементДанных.ТипХраненияФайла = Перечисления.ТипыХраненияФайлов.ВИнформационнойБазе;
		ЭлементДанных.ПутьКФайлу = "";
		ЭлементДанных.Том = Справочники.ТомаХраненияФайлов.ПустаяСсылка();
		
	КонецЕсли;
	
КонецПроцедуры

// Возвращает ссылки на присоединенные файлы для указанного владельца.
//
// Параметры:
//  ВладелецФайлов - ОпределяемыйТип.ПрисоединенныйФайл
//  КромеПомеченныхНаУдаление - Булево
//
// Возвращаемое значение:
//  Массив из ОпределяемыйТип.ПрисоединенныйФайл
//
Функция ПрисоединенныеФайлыКОбъекту(Знач ВладелецФайлов, Знач КромеПомеченныхНаУдаление = Ложь) Экспорт
	
	ИменаСправочников = ИменаСправочниковХраненияФайлов(ВладелецФайлов, Истина);
	Если ИменаСправочников.Количество() = 0 Тогда
		Возврат Новый Массив;
	КонецЕсли;
	
	ТекстЗапросов = "";
	
	Для каждого КлючИЗначение Из ИменаСправочников Цикл
		
		Если ЗначениеЗаполнено(ТекстЗапросов) Тогда
			
			ТекстЗапросов = ТекстЗапросов + "
			|ОБЪЕДИНИТЬ ВСЕ
			|
			|";
			
		КонецЕсли;
		
		ТекстЗапроса =
		"ВЫБРАТЬ
		|	ПрисоединенныеФайлы.Ссылка
		|ИЗ
		|	&ИмяСправочника КАК ПрисоединенныеФайлы
		|ГДЕ
		|	ПрисоединенныеФайлы.ВладелецФайла = &ВладелецФайлов";
		
		Если Метаданные.Справочники[КлючИЗначение.Ключ].Иерархический = Истина Тогда
			ТекстЗапроса = ТекстЗапроса + "
				|	И НЕ ПрисоединенныеФайлы.ЭтоГруппа";
		КонецЕсли;
		Если КромеПомеченныхНаУдаление Тогда
			ТекстЗапроса = ТекстЗапроса + "
				|	И НЕ ПрисоединенныеФайлы.ПометкаУдаления";
		КонецЕсли;
		
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ИмяСправочника", "Справочник." + КлючИЗначение.Ключ);
		ТекстЗапросов = ТекстЗапросов + ТекстЗапроса;
		
	КонецЦикла;
	
	Запрос = Новый Запрос(ТекстЗапросов);
	Запрос.УстановитьПараметр("ВладелецФайлов", ВладелецФайлов);
	
	УстановитьПривилегированныйРежим(Истина);
	Возврат Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Ссылка");
	
КонецФункции

// Возвращает строковую константу для формирования сообщений журнала регистрации.
//
// Возвращаемое значение:
//   Строка
//
Функция СобытиеЖурналаРегистрацииДляОбмена() 
	
	Возврат НСтр("ru = 'Файлы.Не удалось отправить файл при обмене данными'", ОбщегоНазначения.КодОсновногоЯзыка());
	
КонецФункции

// Заменяет двоичные данные файла в ИБ на данные во временном хранилище.
Процедура ОбновитьДвоичныеДанныеФайлаНаСервере(Знач ПрисоединенныйФайл,
	                                           Знач АдресФайлаВоВременномХранилищеДвоичныеДанные,
	                                           Знач РеквизитыЗначения = Неопределено)
	
	УстановитьПривилегированныйРежим(Истина);
	ЭтоСсылка = Справочники.ТипВсеСсылки().СодержитТип(ТипЗнч(ПрисоединенныйФайл));
	
	Контекст = КонтекстОбновленияФайла(ПрисоединенныйФайл, АдресФайлаВоВременномХранилищеДвоичныеДанные);
	МенеджерФайла = МенеджерФайлов(ПрисоединенныйФайл);
	МенеджерФайла.ПередОбновлениемДанныхФайла(Контекст);
	
	НачатьТранзакцию();
	Попытка
		Если ЭтоСсылка Тогда
			БлокировкаДанных = Новый БлокировкаДанных;
			ЭлементБлокировкиДанных = БлокировкаДанных.Добавить(Метаданные.НайтиПоТипу(ТипЗнч(ПрисоединенныйФайл)).ПолноеИмя());
			ЭлементБлокировкиДанных.УстановитьЗначение("Ссылка", ПрисоединенныйФайл);
			БлокировкаДанных.Заблокировать();
			
			ЗаблокироватьДанныеДляРедактирования(ПрисоединенныйФайл);
			
			ФайлОбъект = ПрисоединенныйФайл.ПолучитьОбъект();
		Иначе
			ФайлОбъект = ПрисоединенныйФайл;
		КонецЕсли;
		
		ФайлОбъект.Изменил = Пользователи.АвторизованныйПользователь();
		
		Если ТипЗнч(РеквизитыЗначения) = Тип("Структура") Тогда
			ЗаполнитьЗначенияСвойств(ФайлОбъект, РеквизитыЗначения);
		КонецЕсли;
		
		МенеджерФайла.ПередЗаписьюДанныхФайла(Контекст, ФайлОбъект);
		ФайлОбъект.Записать();
		МенеджерФайла.ПриОбновленииДанныхФайла(Контекст, ФайлОбъект.Ссылка);
		ЗафиксироватьТранзакцию();
		
	Исключение
		ОтменитьТранзакцию();
		МенеджерФайла.ПослеОбновленияДанныхФайла(Контекст, Ложь);
		ВызватьИсключение;
	КонецПопытки;
	
	МенеджерФайла.ПослеОбновленияДанныхФайла(Контекст, Истина);
	
КонецПроцедуры

// Создает версию сохраняемого файла для сохранения в ИБ.
//
// Параметры:
//   ФайлСсылка     - СправочникСсылка.Файлы - файл, для которого создается новая версия.
//   СведенияОФайле - см. РаботаСФайламиКлиентСервер.СведенияОФайле
//   Контекст - см. КонтекстОбновленияФайла
//
// Возвращаемое значение:
//   СправочникСсылка.ВерсииФайлов - созданная версия.
//
Функция СоздатьВерсию(ФайлСсылка, СведенияОФайле, Контекст = Неопределено) Экспорт
	
	ЕстьПраваНаОбъект = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(ФайлСсылка, "Ссылка", Истина);
	Если ЕстьПраваНаОбъект = Неопределено Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	УстановитьПривилегированныйРежим(Истина);
	
	Если Не ЗначениеЗаполнено(СведенияОФайле.ВремяИзмененияУниверсальное)
		Или СведенияОФайле.ВремяИзмененияУниверсальное > ТекущаяУниверсальнаяДата() Тогда
		
		СведенияОФайле.ВремяИзмененияУниверсальное = ТекущаяУниверсальнаяДата();
	КонецЕсли;
	
	Если Не ЗначениеЗаполнено(СведенияОФайле.ВремяИзменения)
		Или УниверсальноеВремя(СведенияОФайле.ВремяИзменения) > СведенияОФайле.ВремяИзмененияУниверсальное Тогда
		
		СведенияОФайле.ВремяИзменения = ТекущаяДатаСеанса();
	КонецЕсли;
	
	ПроверитьРасширениеФайлаДляЗагрузки(СведенияОФайле.РасширениеБезТочки);
	
	Версия = Справочники.ВерсииФайлов.СоздатьЭлемент();
	Если СведенияОФайле.НоваяВерсияНомерВерсии = Неопределено Тогда
		Версия.НомерВерсии = МаксимальныйНомерВерсии(ФайлСсылка) + 1;
	Иначе
		Версия.НомерВерсии = СведенияОФайле.НоваяВерсияНомерВерсии;
	КонецЕсли;
	
	Версия.Владелец                     = ФайлСсылка;
	Версия.ДатаМодификацииУниверсальная = СведенияОФайле.ВремяИзмененияУниверсальное;
	Версия.ДатаМодификацииФайла         = СведенияОФайле.ВремяИзменения;
	Версия.Комментарий                  = СведенияОФайле.НоваяВерсияКомментарий;
	
	Версия.ИндексКартинки = РаботаСФайламиСлужебныйКлиентСервер.ПолучитьИндексПиктограммыФайла(СведенияОФайле.РасширениеБезТочки);
	
	Если СведенияОФайле.НоваяВерсияАвтор = Неопределено Тогда
		Версия.Автор = Пользователи.АвторизованныйПользователь();
	Иначе
		Версия.Автор = СведенияОФайле.НоваяВерсияАвтор;
	КонецЕсли;
	
	Если СведенияОФайле.НоваяВерсияДатаСоздания = Неопределено Тогда
		Версия.ДатаСоздания = ТекущаяДатаСеанса();
	Иначе
		Версия.ДатаСоздания = СведенияОФайле.НоваяВерсияДатаСоздания;
	КонецЕсли;
	
	Версия.Размер             = СведенияОФайле.Размер;
	Версия.Расширение         = ОбщегоНазначенияКлиентСервер.РасширениеБезТочки(СведенияОФайле.РасширениеБезТочки);
	Версия.Наименование       = СведенияОФайле.ИмяБезРасширения;
		
	ТипХраненияФайлов = ТипХраненияФайла(Версия.Размер, Версия.Расширение);
	Версия.ТипХраненияФайла = ТипХраненияФайлов;

	Если СведенияОФайле.СсылкаНаВерсиюИсточник <> Неопределено Тогда // создание Файла из шаблона
		
		ТипХраненияФайловШаблона = СведенияОФайле.СсылкаНаВерсиюИсточник.ТипХраненияФайла;
		Если ТипХраненияФайловШаблона = Перечисления.ТипыХраненияФайлов.ВИнформационнойБазе
			И ТипХраненияФайлов = Перечисления.ТипыХраненияФайлов.ВИнформационнойБазе Тогда
			
			// И шаблон, и новый Файл - в базе.
			// При создании Файла из шаблона хранилище значения копируется напрямую.
			ДвоичныеДанныеИлиПуть = СведенияОФайле.АдресВременногоХранилищаФайла.Получить();
			
		ИначеЕсли ТипХраненияФайловШаблона = Перечисления.ТипыХраненияФайлов.ВТомахНаДиске
			И ТипХраненияФайлов = Перечисления.ТипыХраненияФайлов.ВТомахНаДиске Тогда
			
			//  И шаблон, и новый файл в томе - просто копируем файл.
			Если Не СведенияОФайле.СсылкаНаВерсиюИсточник.Том.Пустая() Тогда
				ПолныйПутьФайлаШаблона = РаботаСФайламиВТомахСлужебный.ПолноеИмяФайлаВТоме(
					РаботаСФайламиВТомахСлужебный.СвойстваФайлаВТоме(СведенияОФайле.СсылкаНаВерсиюИсточник));
				РаботаСФайламиВТомахСлужебный.ДобавитьФайл(Версия, ПолныйПутьФайлаШаблона);
			КонецЕсли;
			
		ИначеЕсли ТипХраненияФайловШаблона = Перечисления.ТипыХраненияФайлов.ВИнформационнойБазе
			И ТипХраненияФайлов = Перечисления.ТипыХраненияФайлов.ВТомахНаДиске Тогда
			
			// Шаблон в базе, новый файл в томе.
			// В этом случае в АдресВременногоХранилищаФайла находится ХранилищеЗначения с файлом.
			РаботаСФайламиВТомахСлужебный.ДобавитьФайл(Версия, СведенияОФайле.АдресВременногоХранилищаФайла.Получить());
			
		ИначеЕсли ТипХраненияФайловШаблона = Перечисления.ТипыХраненияФайлов.ВТомахНаДиске
			И ТипХраненияФайлов = Перечисления.ТипыХраненияФайлов.ВИнформационнойБазе Тогда
			
			// Шаблон в томе, новый файл - в базе.
			Если Не СведенияОФайле.СсылкаНаВерсиюИсточник.Том.Пустая() Тогда
				ДвоичныеДанныеИлиПуть = РаботаСФайламиВТомахСлужебный.ДанныеФайла(СведенияОФайле.СсылкаНаВерсиюИсточник);
			КонецЕсли;
			
		КонецЕсли;
	Иначе // Создание объекта Файл на основе выбранного файла с компьютера.
		
		Если ЭтоАдресВременногоХранилища(СведенияОФайле.АдресВременногоХранилищаФайла) Тогда
			
			ДвоичныеДанныеИлиПуть = ПолучитьИзВременногоХранилища(СведенияОФайле.АдресВременногоХранилищаФайла); // ДвоичныеДанные
			
			Если Версия.Размер = 0 Тогда
				Версия.Размер = ДвоичныеДанныеИлиПуть.Размер();
				ПроверитьРазмерФайлаДляЗагрузки(Версия);
			КонецЕсли;
			
			Если ТипХраненияФайлов = Перечисления.ТипыХраненияФайлов.ВТомахНаДиске Тогда
				Если Контекст <> Неопределено Тогда
					ЗаполнитьЗначенияСвойств(Версия, Контекст.ИзменяемыеРеквизиты);
				Иначе
					РаботаСФайламиВТомахСлужебный.ДобавитьФайл(Версия, ДвоичныеДанныеИлиПуть);
				КонецЕсли;
				
			КонецЕсли;
			
		Иначе
			
			ДвоичныеДанныеИлиПуть = СведенияОФайле.АдресВременногоХранилищаФайла;
			ЗаполнитьЗначенияСвойств(Версия, ФайлСсылка, "Том, ПутьКФайлу");
			
		КонецЕсли;
		
	КонецЕсли;
	
	Версия.СтатусИзвлеченияТекста = Перечисления.СтатусыИзвлеченияТекстаФайлов.НеИзвлечен;
	ИспользованиеПолнотекстовогоПоиска = Метаданные.СвойстваОбъектов.ИспользованиеПолнотекстовогоПоиска.Использовать;
	Если Метаданные.Справочники.ВерсииФайлов.ПолнотекстовыйПоиск = ИспользованиеПолнотекстовогоПоиска Тогда
		Если ТипЗнч(СведенияОФайле.АдресВременногоХранилищаТекста) = Тип("ХранилищеЗначения") Тогда
			// При создании Файла из шаблона хранилище значения копируется напрямую.
			Версия.ТекстХранилище = СведенияОФайле.АдресВременногоХранилищаТекста;
			Версия.СтатусИзвлеченияТекста = Перечисления.СтатусыИзвлеченияТекстаФайлов.Извлечен;
		ИначеЕсли Не ПустаяСтрока(СведенияОФайле.АдресВременногоХранилищаТекста) Тогда
			РезультатИзвлеченияТекста = ИзвлечьТекст(СведенияОФайле.АдресВременногоХранилищаТекста);
			Версия.ТекстХранилище = РезультатИзвлеченияТекста.ТекстХранилище;
			Версия.СтатусИзвлеченияТекста = РезультатИзвлеченияТекста.СтатусИзвлеченияТекста;
		КонецЕсли;
	КонецЕсли;
	
	Версия.Заполнить(Неопределено);
	Версия.Записать();
	
	Если ТипХраненияФайлов = Перечисления.ТипыХраненияФайлов.ВИнформационнойБазе Тогда
		ЗаписатьФайлВИнформационнуюБазу(Версия.Ссылка, ДвоичныеДанныеИлиПуть);
	КонецЕсли;
	
	Возврат Версия.Ссылка;
	
КонецФункции

// Обновляет свойства файла БЕЗ учета версий - двоичные данные, текст, дату изменения,
// а также другие необязательные свойства.
//
Процедура ОбновитьФайл(ИнформацияОФайле, ПрисоединенныйФайл) Экспорт
	
	ОбщегоНазначенияКлиентСервер.ПроверитьПараметр("РаботаСФайлами.ДвоичныеДанныеФайла", "ПрисоединенныйФайл", 
		ПрисоединенныйФайл, Метаданные.ОпределяемыеТипы.ПрисоединенныйФайл.Тип);
	
	РеквизитыЗначения = Новый Структура;
	
	Если ИнформацияОФайле.Свойство("ИмяБезРасширения") И ЗначениеЗаполнено(ИнформацияОФайле.ИмяБезРасширения) Тогда
		РеквизитыЗначения.Вставить("Наименование", ИнформацияОФайле.ИмяБезРасширения);
	КонецЕсли;
	
	Если НЕ ИнформацияОФайле.Свойство("ДатаМодификацииУниверсальная")
		ИЛИ НЕ ЗначениеЗаполнено(ИнформацияОФайле.ДатаМодификацииУниверсальная)
		ИЛИ ИнформацияОФайле.ДатаМодификацииУниверсальная > ТекущаяУниверсальнаяДата() Тогда
		
		// Заполнение текущей даты в формате универсального времени.
		РеквизитыЗначения.Вставить("ДатаМодификацииУниверсальная", ТекущаяУниверсальнаяДата());
	Иначе
		РеквизитыЗначения.Вставить("ДатаМодификацииУниверсальная", ИнформацияОФайле.ДатаМодификацииУниверсальная);
	КонецЕсли;
	
	Если ИнформацияОФайле.Свойство("Редактирует") Тогда
		РеквизитыЗначения.Вставить("Редактирует", ИнформацияОФайле.Редактирует);
	КонецЕсли;
	
	Если ИнформацияОФайле.Свойство("Расширение") Тогда
		РеквизитыЗначения.Вставить("Расширение", ИнформацияОФайле.Расширение);
	КонецЕсли;
	
	Если ИнформацияОФайле.Свойство("Кодировка") И Не ПустаяСтрока(ИнформацияОФайле.Кодировка) Тогда
		РегистрыСведений.КодировкиФайлов.ЗаписатьКодировкуВерсииФайла(ПрисоединенныйФайл, ИнформацияОФайле.Кодировка);
	КонецЕсли;
	
	ДвоичныеДанные = ПолучитьИзВременногоХранилища(ИнформацияОФайле.АдресФайлаВоВременномХранилище);
	
	МетаданныеФайла = Метаданные.НайтиПоТипу(ТипЗнч(ПрисоединенныйФайл));
	ИспользованиеПолнотекстовогоПоиска = Метаданные.СвойстваОбъектов.ИспользованиеПолнотекстовогоПоиска.Использовать;
	Если МетаданныеФайла.ПолнотекстовыйПоиск = ИспользованиеПолнотекстовогоПоиска Тогда
		РезультатИзвлеченияТекста = ИзвлечьТекст(ИнформацияОФайле.АдресВременногоХранилищаТекста, ДвоичныеДанные,
			ПрисоединенныйФайл.Расширение);
		РеквизитыЗначения.Вставить("СтатусИзвлеченияТекста", РезультатИзвлеченияТекста.СтатусИзвлеченияТекста);
		РеквизитыЗначения.Вставить("ТекстХранилище", РезультатИзвлеченияТекста.ТекстХранилище);
	Иначе
		РеквизитыЗначения.Вставить("СтатусИзвлеченияТекста", Перечисления.СтатусыИзвлеченияТекстаФайлов.НеИзвлечен);
		РеквизитыЗначения.Вставить("ТекстХранилище", Новый ХранилищеЗначения(""));
	КонецЕсли;
	
	ОбновитьДвоичныеДанныеФайлаНаСервере(ПрисоединенныйФайл, ДвоичныеДанные, РеквизитыЗначения);
	
КонецПроцедуры

// Обновляет или создает версию Файла и возвращает ссылку на обновленную версию (или Ложь, если файл бинарно не
// изменен).
//
// Параметры:
//   ФайлСсылка     - СправочникСсылка.Файлы        - файл, для которого создается новая версия.
//   СведенияОФайле - см. РаботаСФайламиКлиентСервер.СведенияОФайле
//   ВерсияСсылка   - СправочникСсылка.ВерсииФайлов - версия файла, которую надо обновить.
//   УникальныйИдентификаторФормы                   - УникальныйИдентификатор - уникальный идентификатор формы, 
//                                                    в контексте которой выполняется операция.
//
// Возвращаемое значение:
//   СправочникСсылка.ВерсииФайлов - созданная или измененная версия; Неопределено, если файл бинарно не был изменен.
//
Функция ОбновитьВерсиюФайла(ФайлСсылка,
	СведенияОФайле,
	ВерсияСсылка = Неопределено,
	УникальныйИдентификаторФормы = Неопределено,
	Пользователь = Неопределено) Экспорт
	
	ЕстьПравоСохранения = ПравоДоступа("СохранениеДанныхПользователя", Метаданные);
	ЕстьПраваНаОбъект = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(ФайлСсылка, "Ссылка", Истина);
	Если ЕстьПраваНаОбъект = Неопределено Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	УстановитьПривилегированныйРежим(Истина);
	
	ВремяИзмененияУниверсальное = СведенияОФайле.ВремяИзмененияУниверсальное;
	Если НЕ ЗначениеЗаполнено(ВремяИзмененияУниверсальное)
		ИЛИ ВремяИзмененияУниверсальное > ТекущаяУниверсальнаяДата() Тогда
		ВремяИзмененияУниверсальное = ТекущаяУниверсальнаяДата();
	КонецЕсли;
	
	ВремяИзменения = СведенияОФайле.ВремяИзменения;
	Если НЕ ЗначениеЗаполнено(ВремяИзменения)
		ИЛИ УниверсальноеВремя(ВремяИзменения) > ВремяИзмененияУниверсальное Тогда
		ВремяИзменения = ТекущаяДатаСеанса();
	КонецЕсли;
	
	ПроверитьРасширениеФайлаДляЗагрузки(СведенияОФайле.РасширениеБезТочки);
	
	ДвоичныеДанные = Неопределено;
	МетаданныеОбъекта = Метаданные.НайтиПоТипу(ТипЗнч(ФайлСсылка));
	ВозможностьХранитьВерсии = ОбщегоНазначения.ЕстьРеквизитОбъекта("ТекущаяВерсия", МетаданныеОбъекта);
	ИспользованиеПолнотекстовогоПоиска = Метаданные.СвойстваОбъектов.ИспользованиеПолнотекстовогоПоиска.Использовать;
	
	ВерсияСсылкаДляСравненияРазмера = ВерсияСсылка;
	Если ВерсияСсылка <> Неопределено Тогда
		ВерсияСсылкаДляСравненияРазмера = ВерсияСсылка;
	ИначеЕсли ВозможностьХранитьВерсии И ЗначениеЗаполнено(ТекущаяВерсия(ФайлСсылка))Тогда
		ВерсияСсылкаДляСравненияРазмера = ТекущаяВерсия(ФайлСсылка);
	Иначе
		ВерсияСсылкаДляСравненияРазмера = ФайлСсылка;
	КонецЕсли;
	
	КодировкаПредВерсии = РегистрыСведений.КодировкиФайлов.КодировкаВерсииФайла(ВерсияСсылкаДляСравненияРазмера);
	
	СтруктураРеквизитов = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(ВерсияСсылкаДляСравненияРазмера, 
		"Размер, ТипХраненияФайла, Том, ПутьКФайлу");
	
	ХранилищеФайла = Неопределено;
	Если СведенияОФайле.Размер = СтруктураРеквизитов.Размер Тогда
		
		Если СтруктураРеквизитов.ТипХраненияФайла = Перечисления.ТипыХраненияФайлов.ВТомахНаДиске Тогда
			ДвоичныеДанныеПредыдущейВерсии = РаботаСФайламиВТомахСлужебный.ДанныеФайла(ВерсияСсылкаДляСравненияРазмера, Ложь);
		Иначе
			ХранилищеФайла = РаботаСФайлами.ХранилищеФайлаИзИнформационнойБазы(ВерсияСсылкаДляСравненияРазмера);
			ДвоичныеДанныеПредыдущейВерсии = ХранилищеФайла.Получить();
		КонецЕсли;
		
		ДвоичныеДанные = ПолучитьИзВременногоХранилища(СведенияОФайле.АдресВременногоХранилищаФайла);
		
		Если ДвоичныеДанныеПредыдущейВерсии = ДвоичныеДанные Тогда
			Возврат Неопределено; // Файл бинарно не изменен - вернем Ложь.
		КонецЕсли;
		
	КонецЕсли;
	
	БлокироватьВерсию = Ложь;
	Версия = Неопределено;
	
	Если СведенияОФайле.ХранитьВерсии Тогда
			
		ЗаголовокОшибки = НСтр("ru = 'Ошибка при записи новой версии присоединенных файлов.'");
		ОкончаниеОшибки = НСтр("ru = 'В этом случае запись версии файла невозможна.'");
		
		ЗначенияРеквизитовФайла = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(ФайлСсылка, "ВладелецФайла,ТекущаяВерсия");
		
		ИмяСправочникаХранилищаВерсийФайлов = ИмяСправочникаХраненияВерсийФайлов(
			ТипЗнч(ЗначенияРеквизитовФайла.ВладелецФайла), "", ЗаголовокОшибки, ОкончаниеОшибки);
		
		Версия = Справочники[ИмяСправочникаХранилищаВерсийФайлов].СоздатьЭлемент();
		Версия.РодительскаяВерсия = ЗначенияРеквизитовФайла.ТекущаяВерсия;
		Версия.НомерВерсии = МаксимальныйНомерВерсии(ФайлСсылка) + 1;
		СсылкаНаВерсию = Справочники[ИмяСправочникаХранилищаВерсийФайлов].ПолучитьСсылку(Новый УникальныйИдентификатор());
		Версия.УстановитьСсылкуНового(СсылкаНаВерсию);
		
	Иначе
		
		СсылкаНаВерсию = ?(ВерсияСсылка = Неопределено, ТекущаяВерсия(ФайлСсылка), ВерсияСсылка);
		Версия = СсылкаНаВерсию.ПолучитьОбъект();
		БлокироватьВерсию = Истина;

	КонецЕсли;
	
	Версия.Владелец = ФайлСсылка;
	Если Пользователь = Неопределено Тогда
		Версия.Автор = Пользователи.АвторизованныйПользователь();
	Иначе
		Версия.Автор = Пользователь;
	КонецЕсли;
	Версия.ДатаМодификацииУниверсальная = ВремяИзмененияУниверсальное;
	Версия.ДатаМодификацииФайла = ВремяИзменения;
	Версия.ДатаСоздания = ТекущаяДатаСеанса();
	Версия.Размер = СведенияОФайле.Размер;
	Версия.Наименование = СведенияОФайле.ИмяБезРасширения;
	Версия.Комментарий = СведенияОФайле.Комментарий;
	Версия.Расширение = ОбщегоНазначенияКлиентСервер.РасширениеБезТочки(СведенияОФайле.РасширениеБезТочки);
	
	ТипХраненияФайла = ТипХраненияФайла(СведенияОФайле.Размер, СведенияОФайле.РасширениеБезТочки);
	Версия.ТипХраненияФайла = ТипХраненияФайла;
	
	Если ДвоичныеДанные = Неопределено Тогда
		ДвоичныеДанные = ДвоичныеДанныеИзСведенийОФайле(СведенияОФайле);
	КонецЕсли;
	
	Если Версия.Размер = 0 Тогда
		Версия.Размер = ДвоичныеДанные.Размер();
		ПроверитьРазмерФайлаДляЗагрузки(Версия);
	КонецЕсли;
		
	Если ТипХраненияФайла = Перечисления.ТипыХраненияФайлов.ВИнформационнойБазе Тогда
		Версия.ПутьКФайлу = "";
		Версия.Том = Справочники.ТомаХраненияФайлов.ПустаяСсылка();
	КонецЕсли;
	
	Если СведенияОФайле.Зашифрован = Ложь И МетаданныеОбъекта.ПолнотекстовыйПоиск = ИспользованиеПолнотекстовогоПоиска Тогда
		РезультатИзвлеченияТекста = ИзвлечьТекст(СведенияОФайле.АдресВременногоХранилищаТекста);
		Версия.ТекстХранилище = РезультатИзвлеченияТекста.ТекстХранилище;
		Версия.СтатусИзвлеченияТекста = РезультатИзвлеченияТекста.СтатусИзвлеченияТекста;
		Если СведенияОФайле.НовыйСтатусИзвлеченияТекста <> Неопределено Тогда
			Версия.СтатусИзвлеченияТекста = СведенияОФайле.НовыйСтатусИзвлеченияТекста;
		КонецЕсли;
	Иначе
		Версия.ТекстХранилище = Новый ХранилищеЗначения("");
		Версия.СтатусИзвлеченияТекста = Перечисления.СтатусыИзвлеченияТекстаФайлов.НеИзвлечен;
	КонецЕсли;
	
	Версия.Заполнить(Неопределено);
	
	Контекст = КонтекстОбновленияФайла(Версия, СведенияОФайле.АдресВременногоХранилищаФайла, СсылкаНаВерсию, ТипХраненияФайла);
	МенеджерФайлов = МенеджерФайловПоТипу(ТипХраненияФайла);
	МенеджерФайлов.ПередОбновлениемДанныхФайла(Контекст);
	НачатьТранзакцию();
	Попытка
		
		Если БлокироватьВерсию Тогда
			Блокировка = Новый БлокировкаДанных;
			ЭлементБлокировки = Блокировка.Добавить(СсылкаНаВерсию.Метаданные().ПолноеИмя());
			ЭлементБлокировки.УстановитьЗначение("Ссылка", СсылкаНаВерсию);
			Блокировка.Заблокировать();
			ЗаблокироватьДанныеДляРедактирования(СсылкаНаВерсию, , УникальныйИдентификаторФормы);
		КонецЕсли;
		
		МенеджерФайлов.ПередЗаписьюДанныхФайла(Контекст, Версия);
		Версия.Записать();
		МенеджерФайлов.ПриОбновленииДанныхФайла(Контекст, Версия.Ссылка);
		
		РегистрыСведений.КодировкиФайлов.ЗаписатьКодировкуВерсииФайла(Версия.Ссылка, СведенияОФайле.Кодировка);
		Если СведенияОФайле.ХранитьВерсии Тогда
			ОбновитьВерсиюВФайле(ФайлСсылка, Версия.Ссылка, СведенияОФайле.АдресВременногоХранилищаТекста, УникальныйИдентификаторФормы);
		Иначе
			ОбновитьТекстВФайле(ФайлСсылка, СведенияОФайле.АдресВременногоХранилищаТекста, УникальныйИдентификаторФормы);
		КонецЕсли;
		
		РегистрыСведений.КодировкиФайлов.ЗаписатьКодировкуВерсииФайла(Версия.Ссылка, КодировкаПредВерсии);
		Если БлокироватьВерсию Тогда
			РазблокироватьДанныеДляРедактирования(СсылкаНаВерсию, УникальныйИдентификаторФормы);
		КонецЕсли;
		ЗафиксироватьТранзакцию();
		
	Исключение
		ОтменитьТранзакцию();
		Если БлокироватьВерсию Тогда
			РазблокироватьДанныеДляРедактирования(СсылкаНаВерсию, УникальныйИдентификаторФормы);
		КонецЕсли;
		МенеджерФайлов.ПослеОбновленияДанныхФайла(Контекст, Ложь);
		ВызватьИсключение;
	КонецПопытки;

	МенеджерФайлов.ПослеОбновленияДанныхФайла(Контекст, Истина);
	
	Если ЕстьПравоСохранения Тогда
		НавигационнаяСсылкаФайла = ПолучитьНавигационнуюСсылку(ФайлСсылка);
		ИсторияРаботыПользователя.Добавить(НавигационнаяСсылкаФайла);
	КонецЕсли;
	
	Возврат Версия.Ссылка;
	
КонецФункции

// Параметры:
//   ФайлСсылка - СправочникСсылка.Файлы
//   АдресВременногоХранилищаТекста - Строка - адрес во временном хранилище, где находятся двоичные данные.
//                                  - ХранилищеЗначения
//   УникальныйИдентификатор - УникальныйИдентификатор - уникальный идентификатор формы.
//
Процедура ОбновитьТекстВФайле(ФайлСсылка,
	Знач АдресВременногоХранилищаТекста, УникальныйИдентификатор = Неопределено) Экспорт
	
	ИспользованиеПолнотекстовогоПоиска = Метаданные.СвойстваОбъектов.ИспользованиеПолнотекстовогоПоиска.Использовать;
	МетаданныеСправочника = Метаданные.НайтиПоТипу(ТипЗнч(ФайлСсылка));
	Если МетаданныеСправочника.ПолнотекстовыйПоиск = ИспользованиеПолнотекстовогоПоиска Тогда
		РезультатИзвлеченияТекста = ИзвлечьТекст(АдресВременногоХранилищаТекста);
		ИзвлеченныйТекст = РезультатИзвлеченияТекста.ТекстХранилище;
		СтатусИзвлеченияТекста = РезультатИзвлеченияТекста.СтатусИзвлеченияТекста;
	Иначе
		СтатусИзвлеченияТекста = Перечисления.СтатусыИзвлеченияТекстаФайлов.НеИзвлечен;
		ИзвлеченныйТекст = Новый ХранилищеЗначения("");
	КонецЕсли;
	
	БлокировкаДанных = Новый БлокировкаДанных;
	ЭлементБлокировкиДанных = БлокировкаДанных.Добавить(МетаданныеСправочника.ПолноеИмя());
	ЭлементБлокировкиДанных.УстановитьЗначение("Ссылка", ФайлСсылка);
	
	НачатьТранзакцию();
	Попытка
		БлокировкаДанных.Заблокировать(); 
		
		ФайлОбъект = ФайлСсылка.ПолучитьОбъект();
		ЗаблокироватьДанныеДляРедактирования(ФайлСсылка, , УникальныйИдентификатор);
		
		ФайлОбъект.СтатусИзвлеченияТекста = СтатусИзвлеченияТекста;
		ФайлОбъект.ТекстХранилище = ИзвлеченныйТекст;
		ФайлОбъект.Записать();

		РазблокироватьДанныеДляРедактирования(ФайлСсылка, УникальныйИдентификатор);
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		РазблокироватьДанныеДляРедактирования(ФайлСсылка, УникальныйИдентификатор);
		ВызватьИсключение;
	КонецПопытки
	
КонецПроцедуры

// Подставляет ссылку на версию в карточку Файла.
//
// Параметры:
//   ФайлСсылка - СправочникСсылка.Файлы - файл, в котором создается версия.
//   Версия  - СправочникСсылка.ВерсииФайлов - версия файла.
//   АдресВременногоХранилищаТекста - Строка - содержит адрес во временном хранилище, где находятся двоичные данные с
//                                           файлом текста или ХранилищеЗначения - непосредственно содержит  двоичные
//                                           данные с файлом текста.
//  УникальныйИдентификатор - УникальныйИдентификатор - уникальный идентификатор формы.
//
Процедура ОбновитьВерсиюВФайле(ФайлСсылка,
								Версия,
								Знач АдресВременногоХранилищаТекста,
								УникальныйИдентификатор = Неопределено) Экспорт
	
	ИспользованиеПолнотекстовогоПоиска = Метаданные.СвойстваОбъектов.ИспользованиеПолнотекстовогоПоиска.Использовать;
	
	НачатьТранзакцию();
	Попытка
		
		МетаданныеСправочника = Метаданные.НайтиПоТипу(ТипЗнч(ФайлСсылка));
		
		БлокировкаДанных = Новый БлокировкаДанных;
		ЭлементБлокировкиДанных = БлокировкаДанных.Добавить(МетаданныеСправочника.ПолноеИмя());
		ЭлементБлокировкиДанных.УстановитьЗначение("Ссылка", ФайлСсылка);
		БлокировкаДанных.Заблокировать();
		
		ЗаблокироватьДанныеДляРедактирования(ФайлСсылка, , УникальныйИдентификатор);
		
		ФайлОбъект = ФайлСсылка.ПолучитьОбъект();
		ХранениеВерсии = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(Версия, "ТипХраненияФайла, Том, ПутьКФайлу");
		
		ФайлОбъект.ТекущаяВерсия = Версия;
		Если АдресВременногоХранилищаТекста <> Неопределено
			И МетаданныеСправочника.ПолнотекстовыйПоиск = ИспользованиеПолнотекстовогоПоиска Тогда
			
			Если ТипЗнч(АдресВременногоХранилищаТекста) = Тип("ХранилищеЗначения") Тогда
				// При создании Файла из шаблона хранилище значения копируется напрямую.
				ФайлОбъект.ТекстХранилище = АдресВременногоХранилищаТекста;
			Иначе
				РезультатИзвлеченияТекста = ИзвлечьТекст(АдресВременногоХранилищаТекста);
				ФайлОбъект.ТекстХранилище = РезультатИзвлеченияТекста.ТекстХранилище;
				ФайлОбъект.СтатусИзвлеченияТекста = РезультатИзвлеченияТекста.СтатусИзвлеченияТекста;
			КонецЕсли;
			
		Иначе
			ФайлОбъект.СтатусИзвлеченияТекста = Перечисления.СтатусыИзвлеченияТекстаФайлов.НеИзвлечен;
			ФайлОбъект.ТекстХранилище = Новый ХранилищеЗначения("");
		КонецЕсли;
		
		ЗаполнитьЗначенияСвойств(ФайлОбъект, ХранениеВерсии);
		ФайлОбъект.Записать();
		
		РазблокироватьДанныеДляРедактирования(ФайлСсылка, УникальныйИдентификатор);
		ЗафиксироватьТранзакцию();
		
	Исключение
		ОтменитьТранзакцию();
		РазблокироватьДанныеДляРедактирования(ФайлСсылка, УникальныйИдентификатор);
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

// Возвращает максимальный номер версии для файла. Если нет версий, то 0.
//
// Параметры:
//  ФайлСсылка  - СправочникСсылка.Файлы
//
// Возвращаемое значение:
//   Число
//
Функция МаксимальныйНомерВерсии(ФайлСсылка)
	
	Запрос = Новый Запрос("ВЫБРАТЬ
	|	ЕСТЬNULL(МАКСИМУМ(Версии.НомерВерсии), 0) КАК МаксимальныйНомер
	|ИЗ
	|	Справочник.ВерсииФайлов КАК Версии
	|ГДЕ
	|	Версии.Владелец = &Файл");
	Запрос.Параметры.Вставить("Файл", ФайлСсылка);
		
	Выборка = Запрос.Выполнить().Выбрать();
	Если Выборка.Следующий() Тогда
		Возврат ?(Выборка.МаксимальныйНомер = Null, 0, Число(Выборка.МаксимальныйНомер));
	КонецЕсли;
	
	Возврат 0;
КонецФункции

// Вызывает исключение, если файл имеет недопустимый размер для загрузки.
Процедура ПроверитьРазмерФайлаДляЗагрузки(Файл) Экспорт
	
	ОбщиеНастройки = РаботаСФайламиСлужебныйПовтИсп.НастройкиРаботыСФайлами().ОбщиеНастройки;
	
	Если ТипЗнч(Файл) = Тип("Файл") Тогда
		Размер = Файл.Размер();
	Иначе
		Размер = Файл.Размер;
	КонецЕсли;
	
	Если Размер > ОбщиеНастройки.МаксимальныйРазмерФайла Тогда
	
		РазмерВМб     = Размер / (1024 * 1024);
		РазмерВМбМакс = ОбщиеНастройки.МаксимальныйРазмерФайла / (1024 * 1024);
		
		Если ТипЗнч(Файл) = Тип("Файл") Тогда
			Имя = Файл.Имя;
		Иначе
			Имя = ОбщегоНазначенияКлиентСервер.ПолучитьИмяСРасширением(Файл.Наименование, Файл.Расширение);
		КонецЕсли;
		
		ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Размер файла ""%1"" (%2 Мб)
			           |превышает максимально допустимый размер файла (%3 Мб).'"),
			Имя,
			РаботаСФайламиСлужебныйКлиентСервер.ПолучитьСтрокуСРазмеромФайла(РазмерВМб),
			РаботаСФайламиСлужебныйКлиентСервер.ПолучитьСтрокуСРазмеромФайла(РазмерВМбМакс));
	КонецЕсли;
	
КонецПроцедуры

#КонецОбласти

#Область ОбработчикиСобытийФормыЭлементаФайла

// Параметры:
//   Контекст - ФормаКлиентскогоПриложения:
//     * Объект - ОпределяемыйТип.ПрисоединенныйФайлОбъект
//
Процедура ФормаЭлементаПриСозданииНаСервере(Контекст, Отказ, СтандартнаяОбработка, Параметры, ТолькоПросмотр, НастраиватьОбъектФормы = Ложь) Экспорт
	
	Элементы = Контекст.Элементы;
	
	КолонкиМассив = Новый Массив;
	ТаблицаЭлектронныеПодписи = Контекст.РеквизитФормыВЗначение("ЭлектронныеПодписи"); // ТаблицаЗначений
	Для Каждого ОписаниеКолонки Из ТаблицаЭлектронныеПодписи.Колонки Цикл
		КолонкиМассив.Добавить(ОписаниеКолонки.Имя);
	КонецЦикла;
	
	Если ЗначениеЗаполнено(Параметры.ЗначениеКопирования) Тогда
		ОбновлениеИнформационнойБазы.ПроверитьОбъектОбработан(Параметры.ЗначениеКопирования);
		Если Параметры.РежимСоздания = "ИзШаблона" Тогда
			ОбъектЗначение = ЗаполнитьДанныеФайлаПоШаблону(Контекст, Параметры)
		Иначе
			ОбъектЗначение = ЗаполнитьДанныеФайлаИзКопии(Контекст, Параметры);
		КонецЕсли;
	Иначе
		Если ЗначениеЗаполнено(Параметры.ПрисоединенныйФайл) Тогда
			ОбъектЗначение = Параметры.ПрисоединенныйФайл.ПолучитьОбъект();
		ИначеЕсли ЗначениеЗаполнено(Параметры.Ключ) Тогда
			ОбъектЗначение = Параметры.Ключ.ПолучитьОбъект();
		Иначе
			ВызватьИсключение НСтр("ru='Не предусмотрено непосредственное создание файла.'");
		КонецЕсли;
		ОбновлениеИнформационнойБазы.ПроверитьОбъектОбработан(ОбъектЗначение, Контекст);
	КонецЕсли;
	ОбъектЗначение.Заполнить(Неопределено);
	
	МетаданныеСправочника = ОбъектЗначение.Метаданные(); // ОбъектМетаданныхСправочник
	Контекст.ИмяСправочника = МетаданныеСправочника.Имя;
	
	ВозможностьСоздаватьВерсииФайлов = ТипЗнч(ОбъектЗначение.Ссылка) = Тип("СправочникСсылка.Файлы");
	Контекст.ВозможностьСоздаватьВерсииФайлов = ВозможностьСоздаватьВерсииФайлов; 
	
	Если НастраиватьОбъектФормы Тогда
		Элементы.ХранитьВерсии0.Видимость = ВозможностьСоздаватьВерсииФайлов;
		НастроитьОбъектФормы(ОбъектЗначение, Контекст);
	Иначе
		ЗначениеВДанныеФормы(ОбъектЗначение, Контекст.Объект);
		Элементы.ХранитьВерсии.Видимость = ВозможностьСоздаватьВерсииФайлов;
	КонецЕсли;
	
	Контекст.ДатаМодификации = МестноеВремя(Контекст.Объект.ДатаМодификацииУниверсальная);
	
	КриптографияПриСозданииФормыНаСервере(Контекст, Ложь);
	ЗаполнитьСписокПодписей(Контекст, Параметры.ЗначениеКопирования);
	ЗаполнитьСписокШифрования(Контекст, Параметры.ЗначениеКопирования);
	
	ОбщиеНастройки = РаботаСФайламиСлужебныйПовтИсп.НастройкиРаботыСФайлами().ОбщиеНастройки;
	
	РасширениеФайлаВСписке = РаботаСФайламиСлужебныйКлиентСервер.РасширениеФайлаВСписке(
		ОбщиеНастройки.СписокРасширенийТекстовыхФайлов, Контекст.Объект.Расширение);
	
	Если РасширениеФайлаВСписке Тогда
		Если ВозможностьСоздаватьВерсииФайлов И Контекст.Объект.Свойство("ТекущаяВерсия") И ЗначениеЗаполнено(Контекст.Объект.ТекущаяВерсия) Тогда
			ТекущаяВерсияФайла = Контекст.Объект.ТекущаяВерсия;
		Иначе
			ТекущаяВерсияФайла = Контекст.Объект.Ссылка;
		КонецЕсли;
		Если ЗначениеЗаполнено(ТекущаяВерсияФайла) Тогда
			
			КодировкаЗначение = РегистрыСведений.КодировкиФайлов.КодировкаВерсииФайла(ТекущаяВерсияФайла);
			
			СписокКодировок = Кодировки();
			ЭлементСписка = СписокКодировок.НайтиПоЗначению(КодировкаЗначение);
			Если ЭлементСписка = Неопределено Тогда
				Контекст.Кодировка = КодировкаЗначение;
			Иначе
				Контекст.Кодировка = ЭлементСписка.Представление;
			КонецЕсли;
			
		КонецЕсли;
		
		Если Не ЗначениеЗаполнено(Контекст.Кодировка) Тогда
			Контекст.Кодировка = НСтр("ru = 'По умолчанию'");
		КонецЕсли;
		
	Иначе
		Контекст.Элементы.Кодировка.Видимость = Ложь;
	КонецЕсли;
	
	ЭтоСлужебныйФайл = Ложь;
	Если ЕстьРеквизитСлужебный(Контекст.ИмяСправочника) Тогда
		ЭтоСлужебныйФайл = ОбъектЗначение.Служебный;
	КонецЕсли;
	
	Если ЭтоСлужебныйФайл Тогда
		Контекст.ТолькоПросмотр = Истина;
	КонецЕсли;
	
	Элементы.ФормаЗакрыть.Видимость = ЭтоСлужебныйФайл;
	Элементы.ФормаЗакрыть.КнопкаПоУмолчанию = ЭтоСлужебныйФайл;
	Элементы.ДекорацияПояснениеСлужебный.Видимость = ЭтоСлужебныйФайл;
	
	Если ТипЗнч(Контекст.ТекущийПользователь) = Тип("СправочникСсылка.ВнешниеПользователи") Тогда
		ИзменитьФормуДляВнешнегоПользователя(Контекст);
	КонецЕсли;
	
	Если ПолучитьФункциональнуюОпцию("ИспользоватьСинхронизациюФайлов") Тогда
		Контекст.ФайлРедактируетсяВОблаке = ФайлРедактируетсяВОблаке(Контекст.Объект.Ссылка);
	КонецЕсли;
	
	Если ТолькоПросмотр
		ИЛИ НЕ ПравоДоступа("Изменение", Контекст.Объект.Ссылка.Метаданные()) Тогда
		УстановитьКнопкиИзмененияНевидимыми(Контекст.Элементы);
	КонецЕсли;
	
	Если НЕ ТолькоПросмотр
		И НЕ Контекст.Объект.Ссылка.Пустая() И НастраиватьОбъектФормы Тогда
		ЗаблокироватьДанныеДляРедактирования(Контекст.Объект.Ссылка, , Контекст.УникальныйИдентификатор);
	КонецЕсли;
	
	ТипВладельца = ТипЗнч(ОбъектЗначение.ВладелецФайла);
	ЭлементВладелецФайла = Контекст.Элементы.ВладелецФайла; // ПолеФормы
	ЭлементВладелецФайла.Заголовок = ТипВладельца;
	
КонецПроцедуры

// Параметры:
//   Контекст - ФормаКлиентскогоПриложения:
//     * Объект - ОпределяемыйТип.ПрисоединенныйФайлОбъект
//     * ЗначениеКопирования - ОпределяемыйТип.ПрисоединенныйФайл
//   Параметры - Структура:
//     * ЗначениеКопирования - ОпределяемыйТип.ПрисоединенныйФайл
//     * ИмяСправочникаХранилищаФайлов - Строка
//     * ВладелецФайла - ОпределяемыйТип.ВладелецПрисоединенныхФайлов
//
Функция ЗаполнитьДанныеФайлаПоШаблону(Контекст, Параметры)
	
	ДатаСоздания = ТекущаяДатаСеанса();
	КопируемыйОбъект = Параметры.ЗначениеКопирования.ПолучитьОбъект(); // ОпределяемыйТип.ПрисоединенныйФайлОбъект
	Контекст.ЗначениеКопирования = Параметры.ЗначениеКопирования;
	
	МенеджерСправочника = Справочники[Параметры.ИмяСправочникаХранилищаФайлов]; // СправочникМенеджер
	ОбъектЗначение = МенеджерСправочника.СоздатьЭлемент();
	ЗаполнитьЗначенияСвойств(ОбъектЗначение, КопируемыйОбъект,
		"Наименование,
		|Зашифрован,
		|Описание,
		|ПодписанЭП,
		|Размер,
		|Расширение,
		|ВладелецФайла,
		|ТекстХранилище,
		|ПометкаУдаления");
	ОбъектЗначение.ВладелецФайла                = Параметры.ВладелецФайла;
	ОбъектЗначение.ДатаСоздания                 = ДатаСоздания;
	ОбъектЗначение.ДатаМодификацииУниверсальная = УниверсальноеВремя(ДатаСоздания);
	ОбъектЗначение.Автор                        = Пользователи.АвторизованныйПользователь();
	ОбъектЗначение.ТипХраненияФайла             = ТипХраненияФайла(КопируемыйОбъект.Размер, КопируемыйОбъект.Расширение);
	ОбъектЗначение.ХранитьВерсии                = ?(Параметры.ИмяСправочникаХранилищаФайлов = "Файлы",
		КопируемыйОбъект.ХранитьВерсии, Ложь);
	
	Возврат ОбъектЗначение;
	
КонецФункции

Функция ЗаполнитьДанныеФайлаИзКопии(Контекст, Параметры)

	КопируемыйОбъект = Параметры.ЗначениеКопирования.ПолучитьОбъект();
	Контекст.ЗначениеКопирования = Параметры.ЗначениеКопирования;
	
	ОбъектМетаданных = КопируемыйОбъект.Метаданные();
	МенеджерСправочника = Справочники[ОбъектМетаданных.Имя]; // СправочникМенеджер
	ОбъектЗначение = МенеджерСправочника.СоздатьЭлемент();
	
	ИсключаемыеРеквизиты = "Родитель,Владелец,ДатаЗаема,Изменил,Код,ПометкаУдаления,Редактирует,Том,ИмяПредопределенныхДанных,Предопределенный,ПутьКФайлу,СтатусИзвлеченияТекста";
	Если ОбъектМетаданных.Реквизиты.Найти("ТекущаяВерсия") <> Неопределено Тогда
		ИсключаемыеРеквизиты = ИсключаемыеРеквизиты + ",ТекущаяВерсия";
	КонецЕсли;
	
	ЗаполнитьЗначенияСвойств(ОбъектЗначение,КопируемыйОбъект, , ИсключаемыеРеквизиты);
	ОбъектЗначение.Автор            = Пользователи.АвторизованныйПользователь();
	ОбъектЗначение.ТипХраненияФайла = ТипХраненияФайла(ОбъектЗначение.Размер, ОбъектЗначение.Расширение);
	
	Возврат ОбъектЗначение;
	
КонецФункции

Процедура НастроитьОбъектФормы(Знач НовыйОбъект, Контекст)
	
	ТипНовогоОбъекта = Новый Массив;
	ТипНовогоОбъекта.Добавить(ТипЗнч(НовыйОбъект));
	НовыйРеквизит = Новый РеквизитФормы("Объект", Новый ОписаниеТипов(ТипНовогоОбъекта));
	НовыйРеквизит.СохраняемыеДанные = Истина;
	
	ДобавляемыеРеквизиты = Новый Массив;
	ДобавляемыеРеквизиты.Добавить(НовыйРеквизит);
	Контекст.ИзменитьРеквизиты(ДобавляемыеРеквизиты);
	Контекст.ЗначениеВРеквизитФормы(НовыйОбъект, "Объект");

	Для каждого Элемент Из Контекст.Элементы Цикл
		Если ТипЗнч(Элемент) = Тип("ПолеФормы")
			И СтрНачинаетсяС(Элемент.ПутьКДанным, "ОбъектПрототип[0].")
			И СтрЗаканчиваетсяНа(Элемент.Имя, "0") Тогда
			
			ИмяЭлемента = Лев(Элемент.Имя, СтрДлина(Элемент.Имя) -1);
			
			Если Контекст.Элементы.Найти(ИмяЭлемента) <> Неопределено  Тогда
				Продолжить;
			КонецЕсли;
			
			НовыйЭлемент = Контекст.Элементы.Вставить(ИмяЭлемента, ТипЗнч(Элемент), Элемент.Родитель, Элемент);
			НовыйЭлемент.ПутьКДанным = "Объект." + Сред(Элемент.ПутьКДанным, СтрДлина("ОбъектПрототип[0].") + 1);
			
			Если Элемент.Вид = ВидПоляФормы.ПолеФлажка Или Элемент.Вид = ВидПоляФормы.ПолеКартинки Тогда
				ИсключаемыеСвойства = "Имя, ПутьКДанным";
			Иначе
				ИсключаемыеСвойства = "Имя, ПутьКДанным, ВыделенныйТекст, СвязьПоТипу";
			КонецЕсли;
			ЗаполнитьЗначенияСвойств(НовыйЭлемент, Элемент, , ИсключаемыеСвойства);
			Элемент.Видимость = Ложь;
		КонецЕсли;
	КонецЦикла;
	
	Если Не НовыйОбъект.ЭтоНовый() Тогда
		Контекст.НавигационнаяСсылка = ПолучитьНавигационнуюСсылку(НовыйОбъект);
	КонецЕсли;
	
КонецПроцедуры

Процедура ЗаполнитьСписокШифрования(Контекст, Знач Источник = Неопределено) Экспорт
	Если Не ЗначениеЗаполнено(Источник) Тогда
		Источник = Контекст.Объект;
	КонецЕсли;
	
	Контекст.СертификатыШифрования.Очистить();
	
	Если Источник.Зашифрован Тогда
		
		Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ЭлектроннаяПодпись") Тогда
			МодульЭлектроннаяПодпись = ОбщегоНазначения.ОбщийМодуль("ЭлектроннаяПодпись");
			СертификатыШифрования = МодульЭлектроннаяПодпись.СертификатыШифрования(Источник.Ссылка);
			
			Для Каждого СертификатШифрования Из СертификатыШифрования Цикл
				
				НоваяСтрока = Контекст.СертификатыШифрования.Добавить();
				НоваяСтрока.Представление = СертификатШифрования.Представление;
				НоваяСтрока.Отпечаток = СертификатШифрования.Отпечаток;
				НоваяСтрока.ПорядковыйНомер = СертификатШифрования.ПорядковыйНомер;
				
				ДвоичныеДанныеСертификата = СертификатШифрования.Сертификат;
				Если ДвоичныеДанныеСертификата <> Неопределено Тогда
					
					НоваяСтрока.АдресСертификата = ПоместитьВоВременноеХранилище(
						ДвоичныеДанныеСертификата, Контекст.УникальныйИдентификатор);
				КонецЕсли;
			КонецЦикла;
		КонецЕсли;
		
	КонецЕсли;
	
	ТекстЗаголовка = НСтр("ru = 'Разрешено расшифровывать'");
	
	Если Контекст.СертификатыШифрования.Количество() <> 0 Тогда
		ТекстЗаголовка =ТекстЗаголовка + " (" + Формат(Контекст.СертификатыШифрования.Количество(), "ЧГ=") + ")";
	КонецЕсли;
	
	ГруппаСертификатыШифрования = Контекст.Элементы.ГруппаСертификатыШифрования; // ГруппаФормы
	ГруппаСертификатыШифрования.Заголовок = ТекстЗаголовка;
	
КонецПроцедуры

Процедура ЗаполнитьСписокПодписей(Контекст, Знач Источник = Неопределено) Экспорт
	Если Не ЗначениеЗаполнено(Источник) Тогда
		Источник = Контекст.Объект;
	КонецЕсли;
	
	Контекст.ЭлектронныеПодписи.Очистить();
	
	ЭлектронныеПодписи = СписокЭлектронныхПодписей(Источник, Контекст.УникальныйИдентификатор);
	
	Для Каждого ЭлектроннаяПодписьФайла Из ЭлектронныеПодписи Цикл
		
		НоваяСтрока = Контекст.ЭлектронныеПодписи.Добавить();
		ЗаполнитьЗначенияСвойств(НоваяСтрока, ЭлектроннаяПодписьФайла);
		
		РаботаСФайламиСлужебныйКлиентСервер.ЗаполнитьСтатусПодписи(НоваяСтрока, ТекущаяДатаСеанса());
		
		ДвоичныеДанныеСертификата = ЭлектроннаяПодписьФайла.Сертификат.Получить();
		Если ДвоичныеДанныеСертификата <> Неопределено Тогда 
			НоваяСтрока.АдресСертификата = ПоместитьВоВременноеХранилище(
				ДвоичныеДанныеСертификата, Контекст.УникальныйИдентификатор);
		КонецЕсли;
		
	КонецЦикла;
	
	ТекстЗаголовка = НСтр("ru = 'Электронные подписи'");
	
	Если Контекст.ЭлектронныеПодписи.Количество() <> 0 Тогда
		ТекстЗаголовка = ТекстЗаголовка + " (" + Строка(Контекст.ЭлектронныеПодписи.Количество()) + ")";
	КонецЕсли;
	
	ГруппаЭлектронныеПодписи = Контекст.Элементы.ГруппаЭлектронныеПодписи; // ГруппаФормы
	ГруппаЭлектронныеПодписи.Заголовок = ТекстЗаголовка;
	
КонецПроцедуры

Функция СписокЭлектронныхПодписей(Источник, УникальныйИдентификатор)
	
	ЭлектронныеПодписи = Новый Массив;
	
	Если Источник.ПодписанЭП Тогда
		
		Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ЭлектроннаяПодпись") Тогда
			
			МодульЭлектроннаяПодпись = ОбщегоНазначения.ОбщийМодуль("ЭлектроннаяПодпись");
			ЭлектронныеПодписи = МодульЭлектроннаяПодпись.УстановленныеПодписи(Источник.Ссылка, Неопределено, Истина);
			
			// Локализация
			
			МодульМашиночитаемыеДоверенностиФНССлужебный = Неопределено;
			Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.МашиночитаемыеДоверенности") Тогда
				МодульМашиночитаемыеДоверенностиФНССлужебный = ОбщегоНазначения.ОбщийМодуль("МашиночитаемыеДоверенностиФНССлужебный");
			КонецЕсли;
			
			// Конец Локализация
			
			Для Каждого ЭлектроннаяПодписьФайла Из ЭлектронныеПодписи Цикл
				
				ЭлектроннаяПодписьФайла.Вставить("Объект", Источник.Ссылка);
				АдресПодписи = ПоместитьВоВременноеХранилище(ЭлектроннаяПодписьФайла.Подпись, УникальныйИдентификатор);
				ЭлектроннаяПодписьФайла.Вставить("АдресПодписи", АдресПодписи);
				
				// Локализация
				
				Если ЗначениеЗаполнено(ЭлектроннаяПодписьФайла.РезультатПроверкиПодписиПоМЧД)
					И МодульМашиночитаемыеДоверенностиФНССлужебный <> Неопределено Тогда
						
					МодульМашиночитаемыеДоверенностиФНССлужебный.ДобавитьИнформациюМЧД(ЭлектроннаяПодписьФайла, УникальныйИдентификатор);
				КонецЕсли;
				
				// Конец Локализация
				
			КонецЦикла;
	
		КонецЕсли;
		
	КонецЕсли;
	
	Возврат ЭлектронныеПодписи;
	
КонецФункции

Функция СписокПодписейДляОтправки(Источник, УникальныйИдентификатор, ИмяФайла)
	
	ЭлектронныеПодписи = Новый Массив;
	
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ЭлектроннаяПодпись") Тогда
		
		ЭлектронныеПодписи = СписокЭлектронныхПодписей(Источник, УникальныйИдентификатор);
		СоставИмениФайлаДанных = ОбщегоНазначенияКлиентСервер.РазложитьПолноеИмяФайла(ИмяФайла);
		
		МодульЭлектроннаяПодписьСлужебныйКлиентСервер = ОбщегоНазначения.ОбщийМодуль("ЭлектроннаяПодписьСлужебныйКлиентСервер");
		МодульЭлектроннаяПодписьСлужебный             = ОбщегоНазначения.ОбщийМодуль("ЭлектроннаяПодписьСлужебный");
		МодульЭлектроннаяПодпись                      = ОбщегоНазначения.ОбщийМодуль("ЭлектроннаяПодпись");
		
		РасширениеДляФайловПодписи = МодульЭлектроннаяПодпись.ПерсональныеНастройки().РасширениеДляФайловПодписи;
		
		Для Каждого ЭлектроннаяПодписьФайла Из ЭлектронныеПодписи Цикл
			
			ИмяФайлаПодписи = МодульЭлектроннаяПодписьСлужебныйКлиентСервер.ИмяФайлаПодписи(СоставИмениФайлаДанных.ИмяБезРасширения,
				Строка(ЭлектроннаяПодписьФайла.КомуВыданСертификат), РасширениеДляФайловПодписи);
			ЭлектроннаяПодписьФайла.Вставить("ИмяФайла", ИмяФайлаПодписи);
			
			ДанныеПоСертификату = МодульЭлектроннаяПодписьСлужебный.ДанныеПоСертификату(ЭлектроннаяПодписьФайла, УникальныйИдентификатор);
			ЭлектроннаяПодписьФайла.Вставить("АдресСертификата", ДанныеПоСертификату.АдресСертификата);
			
			ИмяФайлаСертификата = МодульЭлектроннаяПодписьСлужебныйКлиентСервер.ИмяФайлаСертификата(СоставИмениФайлаДанных.ИмяБезРасширения,
				Строка(ЭлектроннаяПодписьФайла.КомуВыданСертификат), ДанныеПоСертификату.РасширениеСертификата);
				
			ЭлектроннаяПодписьФайла.Вставить("ИмяФайлаСертификата", ИмяФайлаСертификата);
			
		КонецЦикла;
	КонецЕсли;
	
	Возврат ЭлектронныеПодписи;
	
КонецФункции

Процедура УстановитьКнопкиИзмененияНевидимыми(Элементы)
	
	ИменаКоманд = ПолучитьИменаКомандИзмененияОбъекта();
	
	Для каждого ЭлементФормы Из Элементы Цикл
	
		Если ТипЗнч(ЭлементФормы) <> Тип("КнопкаФормы") Тогда
			Продолжить;
		КонецЕсли;
		
		Если ИменаКоманд.Найти(ЭлементФормы.ИмяКоманды) <> Неопределено Тогда
			ЭлементФормы.Видимость = Ложь;
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

Функция ПолучитьИменаКомандИзмененияОбъекта()
	
	ИменаКоманд = Новый Массив;
	
	ИменаКоманд.Добавить("ПодписатьФайлЭП");
	ИменаКоманд.Добавить("ДобавитьЭПИзФайла");
	
	ИменаКоманд.Добавить("УдалитьЭП");
	ИменаКоманд.Добавить("ПродлитьДействиеПодписей");
	
	ИменаКоманд.Добавить("Редактировать");
	ИменаКоманд.Добавить("СохранитьИзменения");
	ИменаКоманд.Добавить("ЗакончитьРедактирование");
	ИменаКоманд.Добавить("Освободить");
	
	ИменаКоманд.Добавить("Зашифровать");
	ИменаКоманд.Добавить("Расшифровать");
	
	ИменаКоманд.Добавить("СтандартныеСкопировать");
	ИменаКоманд.Добавить("ОбновитьИзФайлаНаДиске");
	
	ИменаКоманд.Добавить("СтандартнаяЗаписать");
	ИменаКоманд.Добавить("СтандартнаяЗаписатьИЗакрыть");
	ИменаКоманд.Добавить("СтандартныеУстановитьПометкуУдаления");
	
	Возврат ИменаКоманд;
	
КонецФункции

Функция НастройкиФайлов() Экспорт
	
	НастройкиФайлов = Новый Структура;
	НастройкиФайлов.Вставить("НеОчищатьФайлы",            Новый Массив);
	НастройкиФайлов.Вставить("НеСинхронизироватьФайлы",   Новый Массив);
	НастройкиФайлов.Вставить("НеВыводитьВИнтерфейс",      Новый Массив);
	НастройкиФайлов.Вставить("НеСоздаватьФайлыПоШаблону", Новый Массив);
	НастройкиФайлов.Вставить("ФайлыБезПапок",             Новый Массив);
	
	ИнтеграцияПодсистемБСП.ПриОпределенииОбъектовИсключенияСинхронизацииФайлов(НастройкиФайлов.НеСинхронизироватьФайлы);
	РаботаСФайламиПереопределяемый.ПриОпределенииНастроек(НастройкиФайлов);
	
	Возврат НастройкиФайлов;
	
КонецФункции

// Параметры:
//   Результат - Массив из см. ОписаниеФайла
//
Процедура СформироватьСписокФайловДляОтправкиПоПочте(Результат, ФайлВложение, ИдентификаторФормы) Экспорт
	
	ДанныеФайлаИДвоичныеДанные = РаботаСФайлами.ДанныеФайла(ФайлВложение, ИдентификаторФормы); // см. РаботаСФайлами.ДанныеФайла
	ИмяФайла = ОбщегоНазначенияКлиентСервер.ПолучитьИмяСРасширением(
		ДанныеФайлаИДвоичныеДанные.Наименование, ДанныеФайлаИДвоичныеДанные.Расширение);
	ОбщегоНазначения.СократитьИмяФайла(ИмяФайла);
	
	ОписаниеФайла = ОписаниеФайла(ИмяФайла, ДанныеФайлаИДвоичныеДанные.СсылкаНаДвоичныеДанныеФайла);
	Результат.Добавить(ОписаниеФайла);
	
	Если ФайлВложение.ПодписанЭП Тогда
		СписокПодписей = СписокПодписейДляОтправки(ФайлВложение, ИдентификаторФормы, ИмяФайла);
		Для каждого ЭлектроннаяПодписьФайла Из СписокПодписей Цикл
			ОписаниеФайла = ОписаниеФайла(ЭлектроннаяПодписьФайла.ИмяФайла, ЭлектроннаяПодписьФайла.АдресПодписи);
			Результат.Добавить(ОписаниеФайла);
			
			Если ЗначениеЗаполнено(ЭлектроннаяПодписьФайла.АдресСертификата) Тогда
				ОписаниеФайла = ОписаниеФайла(ЭлектроннаяПодписьФайла.ИмяФайлаСертификата, ЭлектроннаяПодписьФайла.АдресСертификата);
				Результат.Добавить(ОписаниеФайла);
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;
	
КонецПроцедуры

Функция ОписаниеФайла(ИмяФайла, АдресВоВременномХранилище)
	
	ОписаниеФайла = Новый Структура;
	ОписаниеФайла.Вставить("Представление",             ИмяФайла);
	ОписаниеФайла.Вставить("АдресВоВременномХранилище", АдресВоВременномХранилище);
	
	Возврат ОписаниеФайла;
	
КонецФункции

#КонецОбласти

#Область ОчисткаНенужныхФайлов

// Параметры:
//   НенужныеФайлы - см. ВыбратьНенужныеФайлы
//
Процедура ОчиститьДанныеНенужныхФайлов(НенужныеФайлы)
	
	Если НенужныеФайлы.Строки.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	УдаляемыеПрисоединенныеФайлы = Новый Массив;
	Для Каждого Файл Из НенужныеФайлы.Строки Цикл
		ПрисоединенныйФайл = Файл.ФайлСсылка; // ОпределяемыйТип.ПрисоединенныйФайл
		ПодготовленныйФайл = ПодготовитьФайлКУдалению(ПрисоединенныйФайл);
		Если ЗначениеЗаполнено(ПодготовленныйФайл) Тогда
			УдаляемыеПрисоединенныеФайлы.Добавить(ПодготовленныйФайл);
		КонецЕсли;
	КонецЦикла;
	
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.УдалениеПомеченныхОбъектов") Тогда
		МодульУдалениеПомеченныхОбъектов = ОбщегоНазначения.ОбщийМодуль("УдалениеПомеченныхОбъектов");
		РезультатУдаления = МодульУдалениеПомеченныхОбъектов.УдалитьПомеченныеОбъекты(УдаляемыеПрисоединенныеФайлы);
		Если Не РезультатУдаления.Успешно Тогда
			ЗаписатьВЖурналСобытийОчисткиФайлов(СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Не все ненужные файлы были удалены автоматически (%1), так как они используются в других местах в программе, либо по другим причинам.
				|Для просмотра причин невозможности удаления откройте ""Удаление помеченных объектов"" в настройках программы.'"),
				РезультатУдаления.НеУдаленные.Количество()), УровеньЖурналаРегистрации.Предупреждение);
		КонецЕсли;
		Возврат;
	КонецЕсли;
	
	МестаИспользования = ОбщегоНазначения.МестаИспользования(УдаляемыеПрисоединенныеФайлы);
	Для Каждого ПрисоединенныйФайл Из УдаляемыеПрисоединенныеФайлы Цикл
		Фильтр = Новый Структура("Ссылка, ВспомогательныеДанные, ЭтоСлужебныеДанные", ПрисоединенныйФайл, Ложь, Ложь);
		МестаИспользованияФайла = МестаИспользования.НайтиСтроки(Фильтр);
		
		Для Индекс = -МестаИспользованияФайла.ВГраница() По 0 Цикл
			Место = МестаИспользования[-Индекс];
			Если Место.Метаданные = Метаданные.Справочники.ВерсииФайлов Тогда
				МестаИспользованияФайла.Удалить(-Индекс);
			КонецЕсли;
		КонецЦикла;

		ЕстьМестаИспользования = МестаИспользованияФайла.Количество() <> 0;
		Если ЕстьМестаИспользования Тогда
			Продолжить;
		КонецЕсли;
		ПрисоединенныйФайл = НенужныеФайлы.Строки.Найти(ПрисоединенныйФайл, "ФайлСсылка");
		УдалитьНенужныеФайлы(ПрисоединенныйФайл);
	КонецЦикла;

КонецПроцедуры

Функция ПодготовитьФайлКУдалению(ПрисоединенныйФайл)
	Результат = Неопределено;
	
	ЗаписатьВЖурналСобытийОчисткиФайлов(СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		НСтр("ru = 'Удаляется ненужный файл ""%1""...'"), ОбщегоНазначения.ПредметСтрокой(ПрисоединенныйФайл)),,
		ПрисоединенныйФайл);
	Блокировка = Новый БлокировкаДанных();
	ЭлементБлокировки = Блокировка.Добавить(ПрисоединенныйФайл.Метаданные().ПолноеИмя());
	ЭлементБлокировки.УстановитьЗначение("Ссылка", ПрисоединенныйФайл);
		
	НачатьТранзакцию();
	Попытка
		Блокировка.Заблокировать();
		ЗаблокироватьДанныеДляРедактирования(ПрисоединенныйФайл);
		
		ФайлОбъект = ПрисоединенныйФайл.ПолучитьОбъект();
		Если ФайлОбъект = Неопределено Тогда // Уже удален
			ЗафиксироватьТранзакцию();
			Возврат Результат;
		КонецЕсли;
		Если ОбщегоНазначения.ЕстьРеквизитОбъекта("Редактирует", ФайлОбъект.Метаданные())
			И ЗначениеЗаполнено(ФайлОбъект.Редактирует) Тогда
			ЗаписатьВЖурналСобытийОчисткиФайлов(НСтр("ru = 'Файл открыт на редактирование, поэтому не может быть удален.'"),,
				ПрисоединенныйФайл);
			ЗафиксироватьТранзакцию();
			Возврат Результат;
		КонецЕсли;
		
		ФайлОбъект.УстановитьПометкуУдаления(Истина);
		Результат = ПрисоединенныйФайл;
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ЗаписатьВЖурналСобытийОчисткиФайлов(ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()),
			УровеньЖурналаРегистрации.Ошибка, ПрисоединенныйФайл);
	КонецПопытки;
	
	Возврат Результат;
КонецФункции

// Параметры:
//  ОписаниеПрисоединенногоФайла - см. ВыбратьНенужныеФайлы
//
Процедура УдалитьНенужныеФайлы(ОписаниеПрисоединенногоФайла)
	
	УдаляемыеФайлы = Новый Массив;
	
	УдаляемыеПрисоединенныеФайлы = Новый Массив; 
	Для Каждого Версия Из ОписаниеПрисоединенногоФайла.Строки Цикл
		УдаляемыеПрисоединенныеФайлы.Добавить(Версия.ВерсияСсылка);
	КонецЦикла;
	УдаляемыеПрисоединенныеФайлы.Добавить(ОписаниеПрисоединенногоФайла.ФайлСсылка);
	
	Для Каждого ПрисоединенныйФайл Из УдаляемыеПрисоединенныеФайлы Цикл
		
		ТипХраненияФайла = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(ПрисоединенныйФайл, "ТипХраненияФайла");
		Если ТипХраненияФайла = Перечисления.ТипыХраненияФайлов.ВТомахНаДиске Тогда
			СвойстваФайла = РаботаСФайламиВТомахСлужебный.СвойстваФайлаВТоме(ПрисоединенныйФайл);
			УдаляемыеФайлы.Добавить(РаботаСФайламиВТомахСлужебный.ПолноеИмяФайлаВТоме(СвойстваФайла));
		КонецЕсли;
		
		Попытка
			УдалитьНенужныйФайл(ПрисоединенныйФайл);
		Исключение
			Если ТипХраненияФайла = Перечисления.ТипыХраненияФайлов.ВТомахНаДиске
					И УдаляемыеФайлы.Количество() > 0 Тогда
				УдаляемыеФайлы.Удалить(УдаляемыеФайлы.ВГраница());	
			КонецЕсли;
			Продолжить;
		КонецПопытки;	
	КонецЦикла;
	
	Для Каждого Файл Из УдаляемыеФайлы Цикл
		
		Если НЕ ЗначениеЗаполнено(Файл) Тогда
			Продолжить;
		КонецЕсли;
		
		РаботаСФайламиВТомахСлужебный.УдалитьФайл(Файл);
	КонецЦикла;

КонецПроцедуры

// Параметры:
//  ПрисоединенныйФайл - ОпределяемыйТип.ПрисоединенныйФайл
//
Функция УдалитьНенужныйФайл(ПрисоединенныйФайл)
	
	Блокировка = Новый БлокировкаДанных();
	ЭлементБлокировки = Блокировка.Добавить(ПрисоединенныйФайл.Метаданные().ПолноеИмя());
	ЭлементБлокировки.УстановитьЗначение("Ссылка", ПрисоединенныйФайл);
		
	НачатьТранзакцию();
	Попытка
		Блокировка.Заблокировать();
		ЗаблокироватьДанныеДляРедактирования(ПрисоединенныйФайл);
		
		ПрисоединенныйФайлОбъект = ПрисоединенныйФайл.ПолучитьОбъект();
		Если ПрисоединенныйФайлОбъект = Неопределено Тогда // Уже удален
			ЗафиксироватьТранзакцию();
			Возврат Ложь;
		КонецЕсли;	
		Если ТипЗнч(ПрисоединенныйФайл) <> Тип("СправочникСсылка.ВерсииФайлов")
				И ЗначениеЗаполнено(ПрисоединенныйФайлОбъект.Редактирует) Тогда
			ЗафиксироватьТранзакцию();
			Возврат Ложь;
		КонецЕсли;
		
		Том = ПрисоединенныйФайлОбъект.Том;
		Если ЗначениеЗаполнено(Том) Тогда
			ТомОбъект = Том.ПолучитьОбъект();
			ТомОбъект.ВремяПоследнейОчисткиФайлов = ТекущаяУниверсальнаяДата();
			ТомОбъект.Записать();
		КонецЕсли;
		
		ПрисоединенныйФайлОбъект.Удалить();
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ЗаписатьВЖурналСобытийОчисткиФайлов(ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()), 
			УровеньЖурналаРегистрации.Ошибка, ПрисоединенныйФайл);
		ВызватьИсключение;
	КонецПопытки;
	
	Возврат Истина;
КонецФункции

// Параметры:
//  НастройкаОчистки - СтрокаТаблицыЗначений из см. РегистрыСведений.НастройкиОчисткиФайлов.ТекущиеНастройкиОчистки
//  МассивИсключений - Массив из ОпределяемыйТип.ВладелецФайлов
// 
// Возвращаемое значение:
//  ДеревоЗначений:
//    * ФайлСсылка - ОпределяемыйТип.ПрисоединенныйФайл
//    * ВладелецФайла - Тип - тип ОпределяемыйТип.ВладелецФайлов.
//    * Размер - Число - размер в Мб. 
//
Функция ВыбратьНенужныеФайлы(НастройкаОчистки, МассивИсключений) Экспорт
	
	КомпоновщикНастроек = Новый КомпоновщикНастроекКомпоновкиДанных;
	
	ОчищатьПоПравилу = НастройкаОчистки.ПериодОчистки = Перечисления.ПериодОчисткиФайлов.ПоПравилу;
	Если ОчищатьПоПравилу Тогда
		НастройкиКомпоновщика = НастройкаОчистки.ПравилоОтбора.Получить();
		Если НастройкиКомпоновщика <> Неопределено Тогда
			КомпоновщикНастроек.ЗагрузитьНастройки(НастройкаОчистки.ПравилоОтбора.Получить());
		КонецЕсли;
	КонецЕсли;
	
	СхемаКомпоновкиДанных = Новый СхемаКомпоновкиДанных;
	ИсточникДанных = СхемаКомпоновкиДанных.ИсточникиДанных.Добавить();
	ИсточникДанных.Имя = "ИсточникДанных1";
	ИсточникДанных.ТипИсточникаДанных = "Local";
	
	НаборДанных = СхемаКомпоновкиДанных.НаборыДанных.Добавить(Тип("НаборДанныхЗапросСхемыКомпоновкиДанных"));
	НаборДанных.Имя = "НаборДанных1";
	НаборДанных.ИсточникДанных = ИсточникДанных.Имя;
	
	СхемаКомпоновкиДанных.ПоляИтога.Очистить();
	
	Если НастройкаОчистки.ЭтоНастройкаДляЭлементаСправочника Тогда
		ВладелецФайла = НастройкаОчистки.ИдентификаторВладельца;
		ЭлементИсключение = НастройкаОчистки.ВладелецФайла;
	Иначе
		ВладелецФайла = НастройкаОчистки.ВладелецФайла;
		ЭлементИсключение = Неопределено;
	КонецЕсли;
	
	СхемаКомпоновкиДанных.НаборыДанных[0].Запрос = ТекстЗапросаДляОчисткиФайлов(ВладелецФайла,
		НастройкаОчистки, МассивИсключений, ЭлементИсключение);
	
	Структура = КомпоновщикНастроек.Настройки.Структура.Добавить(Тип("ГруппировкаКомпоновкиДанных"));
	
	ВыбранноеПоле = Структура.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
	ВыбранноеПоле.Поле = Новый ПолеКомпоновкиДанных("ФайлСсылка");
	ВыбранноеПоле = Структура.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
	ВыбранноеПоле.Поле = Новый ПолеКомпоновкиДанных("ВладелецФайла");
	ВыбранноеПоле = Структура.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
	ВыбранноеПоле.Поле = Новый ПолеКомпоновкиДанных("Размер");
	
	Если НастройкаОчистки.ЭтоФайл Тогда
		СтруктураВерсий = Структура.Структура.Добавить(Тип("ГруппировкаКомпоновкиДанных"));
		ВыбранноеПоле = СтруктураВерсий.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
		ВыбранноеПоле.Поле = Новый ПолеКомпоновкиДанных("ВерсияСсылка");
	КонецЕсли;
	
	КомпоновщикНастроек.Инициализировать(Новый ИсточникДоступныхНастроекКомпоновкиДанных(СхемаКомпоновкиДанных));
	
	Параметр = КомпоновщикНастроек.Настройки.ПараметрыДанных.Элементы.Найти("ТипВладельца");
	Параметр.Значение = ТипЗнч(ОбщегоНазначения.ЗначениеРеквизитаОбъекта(ВладелецФайла, "ЗначениеПустойСсылки"));
	Параметр.Использование = Истина;
	
	Параметр = КомпоновщикНастроек.Настройки.ПараметрыДанных.Элементы.Найти("ПериодОчистки");
	Если Параметр <> Неопределено Тогда
		Если НастройкаОчистки.ПериодОчистки = Перечисления.ПериодОчисткиФайлов.СтаршеМесяца Тогда
			ЗначениеПериодаОчистки = ДобавитьМесяц(НачалоДня(ТекущаяДатаСеанса()), -1);
		ИначеЕсли НастройкаОчистки.ПериодОчистки = Перечисления.ПериодОчисткиФайлов.СтаршеГода Тогда
			ЗначениеПериодаОчистки = ДобавитьМесяц(НачалоДня(ТекущаяДатаСеанса()), -12);
		ИначеЕсли НастройкаОчистки.ПериодОчистки = Перечисления.ПериодОчисткиФайлов.СтаршеШестиМесяцев Тогда
			ЗначениеПериодаОчистки = ДобавитьМесяц(НачалоДня(ТекущаяДатаСеанса()), -6);
		КонецЕсли;
		Параметр.Значение = ЗначениеПериодаОчистки;
		Параметр.Использование = Истина;
	КонецЕсли;
	
	ПараметрТекущаяДата = КомпоновщикНастроек.Настройки.ПараметрыДанных.Элементы.Найти("ТекущаяДата");
	Если ПараметрТекущаяДата <> Неопределено Тогда
		ПараметрТекущаяДата.Значение = ТекущаяДатаСеанса();
		ПараметрТекущаяДата.Использование = Истина;
	КонецЕсли;
	
	Если МассивИсключений.Количество() > 0 Тогда
		Параметр = КомпоновщикНастроек.Настройки.ПараметрыДанных.Элементы.Найти("МассивИсключений");
		Параметр.Значение = МассивИсключений;
		Параметр.Использование = Истина;
	КонецЕсли;
	
	Если НастройкаОчистки.ЭтоНастройкаДляЭлементаСправочника Тогда
		Параметр = КомпоновщикНастроек.Настройки.ПараметрыДанных.Элементы.Найти("ЭлементИсключение");
		Параметр.Значение = ЭлементИсключение;
		Параметр.Использование = Истина;
	КонецЕсли;
	
	КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
	ПроцессорКомпоновкиДанных = Новый ПроцессорКомпоновкиДанных;
	
	МакетКомпоновкиДанных = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, КомпоновщикНастроек.Настройки, , ,
		Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений"));
	ПроцессорКомпоновкиДанных.Инициализировать(МакетКомпоновкиДанных);
	
	ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
	ПроцессорВывода.УстановитьОбъект(Новый ДеревоЗначений);

	Результат = ПроцессорВывода.Вывести(ПроцессорКомпоновкиДанных);
	Возврат Результат;
	
КонецФункции

// Параметры:
//  ВладелецФайла - СправочникСсылка.ИдентификаторыОбъектовМетаданных
//
Функция РеквизитыВладельцаФайлов(ВладелецФайла)

	РеквизитыЗапроса = "";
	
	ОбъектМетаданных = ОбщегоНазначения.ОбъектМетаданныхПоИдентификатору(ВладелецФайла);
	Если ОбщегоНазначения.ЭтоСправочник(ОбъектМетаданных) Тогда
		Для Каждого Реквизит Из ОбъектМетаданных.Реквизиты Цикл
			РеквизитыЗапроса = РеквизитыЗапроса + Символы.ПС + "СправочникВладелецФайла." + Реквизит.Имя + ",";
		КонецЦикла;
	ИначеЕсли ОбщегоНазначения.ЭтоДокумент(ОбъектМетаданных) Тогда
		ШаблонЗапроса = "РАЗНОСТЬДАТ(&ИмяРеквизитаДата, &ТекущаяДата, ДЕНЬ) Как ДнейДоУдаленияОтДаты"; // @query-part
		Для Каждого Реквизит Из ОбъектМетаданных.Реквизиты Цикл
			Если Реквизит.Тип = Новый ОписаниеТипов("Дата") Тогда
				ФрагментЗапроса = СтрЗаменить(ШаблонЗапроса, "&ИмяРеквизитаДата", "СправочникВладелецФайла." + Реквизит.Имя);
				ФрагментЗапроса = СтрЗаменить(ФрагментЗапроса, "ДнейДоУдаленияОтДаты", "ДнейДоУдаленияОт" + Реквизит.Имя);
				РеквизитыЗапроса = РеквизитыЗапроса + Символы.ПС + ФрагментЗапроса + ",";
			КонецЕсли;
			РеквизитыЗапроса = РеквизитыЗапроса + Символы.ПС + "СправочникВладелецФайла." + Реквизит.Имя + ",";
		КонецЦикла;
	КонецЕсли;
	Возврат РеквизитыЗапроса;
	
КонецФункции

#КонецОбласти

#Область СинхронизацияФайлов

// Возвращаемое значение:
//   ТаблицаЗначений:
//     * Ссылка - СправочникСсылка.УчетныеЗаписиСинхронизацииФайлов
//     * Сервис - Строка
//
Функция УчетныеЗаписиСинхронизации()
	
	Запрос = Новый Запрос;
	Запрос.Текст = "ВЫБРАТЬ РАЗЛИЧНЫЕ
	|	УчетныеЗаписиСинхронизацииФайлов.Ссылка,
	|	УчетныеЗаписиСинхронизацииФайлов.Сервис
	|ИЗ
	|	РегистрСведений.НастройкиСинхронизацииФайлов КАК НастройкиСинхронизацииФайлов
	|		ЛЕВОЕ СОЕДИНЕНИЕ Справочник.УчетныеЗаписиСинхронизацииФайлов КАК УчетныеЗаписиСинхронизацииФайлов
	|		ПО НастройкиСинхронизацииФайлов.УчетнаяЗапись = УчетныеЗаписиСинхронизацииФайлов.Ссылка
	|ГДЕ
	|	НЕ УчетныеЗаписиСинхронизацииФайлов.ПометкаУдаления
	|	И НастройкиСинхронизацииФайлов.Синхронизировать";
	
	Возврат Запрос.Выполнить().Выгрузить();
	
КонецФункции

Процедура УстановитьПараметрРегламентногоЗаданияСинхронизацииФайлов(Знач ИмяПараметра, Знач ЗначениеПараметра) Экспорт
	
	ПараметрыЗадания = Новый Структура;
	ПараметрыЗадания.Вставить("Метаданные", Метаданные.РегламентныеЗадания.СинхронизацияФайлов);
	Если Не ОбщегоНазначения.РазделениеВключено() Тогда
		ПараметрыЗадания.Вставить("ИмяМетода", Метаданные.РегламентныеЗадания.СинхронизацияФайлов.ИмяМетода);
	КонецЕсли;
	
	УстановитьПривилегированныйРежим(Истина);
	
	СписокЗаданий = РегламентныеЗаданияСервер.НайтиЗадания(ПараметрыЗадания);
	Если СписокЗаданий.Количество() = 0 Тогда
		ПараметрыЗадания.Вставить(ИмяПараметра, ЗначениеПараметра);
		РегламентныеЗаданияСервер.ДобавитьЗадание(ПараметрыЗадания);
	Иначе
		ПараметрыЗадания = Новый Структура(ИмяПараметра, ЗначениеПараметра);
		Для Каждого Задание Из СписокЗаданий Цикл
			РегламентныеЗаданияСервер.ИзменитьЗадание(Задание, ПараметрыЗадания);
		КонецЦикла;
	КонецЕсли;

КонецПроцедуры

Функция ЭтоПапкаФайлов(ОбъектВладелец) Экспорт
	
	Возврат ТипЗнч(ОбъектВладелец) = Тип("СправочникСсылка.ПапкиФайлов");
	
КонецФункции

Функция ФайлРедактируетсяВОблаке(Файл)
	
	Запрос = Новый Запрос;
	Запрос.Текст = 
		"ВЫБРАТЬ ПЕРВЫЕ 1
		|	СтатусыСинхронизацииФайловСОблачнымСервисом.Файл
		|ИЗ
		|	РегистрСведений.СтатусыСинхронизацииФайловСОблачнымСервисом КАК СтатусыСинхронизацииФайловСОблачнымСервисом
		|ГДЕ
		|	СтатусыСинхронизацииФайловСОблачнымСервисом.Файл = &Файл";
	
	Запрос.УстановитьПараметр("Файл", Файл);
	
	РезультатЗапроса = Запрос.Выполнить();
	
	ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
	
	Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
		Возврат Истина;
	КонецЦикла;
	
	Возврат Ложь;
	
КонецФункции

Функция ПриОпределенииОбъектовИсключенияСинхронизацииФайлов() Экспорт
	
	Возврат НастройкиФайлов().НеСинхронизироватьФайлы;
	
КонецФункции

Функция ТекстЗапросаДляСинхронизацииФайлов(ВладелецФайла, НастройкаСинхронизации, МассивИсключений, ЭлементИсключение)
	
	ПредставлениеТипаВладельца = ОбщегоНазначения.ВидОбъектаПоТипу(ТипЗнч(ВладелецФайла.ЗначениеПустойСсылки));
	ПолноеИмяСправочникаФайлов = НастройкаСинхронизации.ТипВладельцаФайла.ПолноеИмя;
	МетаданныеОбъектаФайлов = ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(ПолноеИмяСправочникаФайлов);
	ЕстьВозможностьХранитьВерсии = ОбщегоНазначения.ЕстьРеквизитОбъекта("ТекущаяВерсия", МетаданныеОбъектаФайлов);
	
	ТекстЗапроса = "";
	
	СправочникФайлов = ОбщегоНазначения.ОбъектМетаданныхПоИдентификатору(НастройкаСинхронизации.ТипВладельцаФайла, Ложь);
	Если ТипЗнч(СправочникФайлов) <> Тип("ОбъектМетаданных") Тогда
		Возврат "";
	КонецЕсли;
	ВозможностьСоздаватьГруппы = СправочникФайлов.Иерархический;
	
	Если ТипЗнч(ВладелецФайла) <> Тип("СправочникСсылка.ИдентификаторыОбъектовМетаданных") Тогда
		СправочникПапок = ОбщегоНазначения.ОбъектМетаданныхПоИдентификатору(НастройкаСинхронизации.ИдентификаторВладельца, Ложь);
	Иначе
		СправочникПапок = ОбщегоНазначения.ОбъектМетаданныхПоИдентификатору(ВладелецФайла, Ложь);
	КонецЕсли;
	
	Если ТипЗнч(СправочникПапок) <> Тип("ОбъектМетаданных") Тогда
		Возврат "";
	КонецЕсли;
		
	ТекстЗапроса = ТекстЗапроса + "ВЫБРАТЬ
	|	СправочникПапок.Ссылка,"; // @query-part
	
	ДобавитьДоступныеПоляОтбора(ТекстЗапроса, ВладелецФайла);
	
	ТекстЗапроса = ТекстЗапроса + "
	|	СправочникФайлов.Ссылка КАК ФайлСсылка,"; // @query-part
	
	Если ВозможностьСоздаватьГруппы Тогда
		
		ТекстЗапроса = ТекстЗапроса + "
		|	ВЫБОР Когда СправочникФайлов.ЭтоГруппа Тогда
		|		СправочникФайлов.Наименование
		|	ИНАЧЕ
		|		СправочникФайлов.Наименование + ""."" + СправочникФайлов.Расширение
		|	КОНЕЦ КАК Наименование,
		|	ВЫБОР Когда СправочникФайлов.ЭтоГруппа Тогда
		|		""""
		|	ИНАЧЕ
		|		СправочникФайлов.Расширение
		|	КОНЕЦ КАК Расширение,
		|	ВЫБОР Когда СправочникФайлов.ЭтоГруппа Тогда
		|		ЛОЖЬ
		|	ИНАЧЕ
		|		СправочникФайлов.ПодписанЭП
		|	КОНЕЦ КАК ПодписанЭП,
		|	ВЫБОР Когда СправочникФайлов.ЭтоГруппа Тогда
		|		ЛОЖЬ
		|	ИНАЧЕ
		|		СправочникФайлов.Зашифрован
		|	КОНЕЦ КАК Зашифрован,
		|	СправочникФайлов.ПометкаУдаления КАК ПометкаУдаления,
		|	СправочникФайлов.ВладелецФайла КАК Родитель,
		|	ЛОЖЬ КАК ЭтоПапка,"; // @query-part
		
	Иначе
		
		ТекстЗапроса = ТекстЗапроса + "
		|	СправочникФайлов.Наименование + ""."" + СправочникФайлов.Расширение КАК Наименование,
		|	СправочникФайлов.Расширение КАК Расширение,
		|	СправочникФайлов.ПодписанЭП КАК ПодписанЭП,
		|	СправочникФайлов.Зашифрован КАК Зашифрован,
		|	СправочникФайлов.ПометкаУдаления КАК ПометкаУдаления,
		|	СправочникФайлов.ВладелецФайла КАК Родитель,
		|	ЛОЖЬ КАК ЭтоПапка,"; // @query-part
	
	КонецЕсли;
	
	ТекстЗапроса = ТекстЗапроса + "
	|	ИСТИНА КАК ЕстьВБазе,
	|	ЛОЖЬ КАК ЕстьНаСервере,
	|	НЕОПРЕДЕЛЕНО КАК Изменения,
	|	ЕСТЬNULL(СтатусыСинхронизацииФайловСОблачнымСервисом.Href, """") КАК Href,
	|	ЕСТЬNULL(СтатусыСинхронизацииФайловСОблачнымСервисом.Etag, """") КАК Etag,
	|	ЛОЖЬ КАК Обработан,
	|	ДАТАВРЕМЯ(1, 1, 1, 0, 0, 0) КАК ДатаСинхронизации,
	|	ВЫРАЗИТЬ("""" КАК СТРОКА(36)) КАК UID1C,
	|	"""" КАК ToHref,
	|	"""" КАК ToEtag,
	|	"""" КАК РодительСервер,
	|	"""" КАК НаименованиеСервер,
	|	ЛОЖЬ КАК ИзмененНаСервере,
	|	ЛОЖЬ КАК ЗашифрованНаСервере,
	|	НЕОПРЕДЕЛЕНО КАК Уровень,
	|	"""" КАК РодительСортировка,
	|	" + ?(ЕстьВозможностьХранитьВерсии, "ИСТИНА", "ЛОЖЬ") + " КАК ЭтоФайл
	|ИЗ
	|	Справочник." + СправочникФайлов.Имя + " КАК СправочникФайлов
	|		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.СтатусыСинхронизацииФайловСОблачнымСервисом КАК СтатусыСинхронизацииФайловСОблачнымСервисом
	|		ПО (СтатусыСинхронизацииФайловСОблачнымСервисом.Файл = СправочникФайлов.Ссылка)
	|		ЛЕВОЕ СОЕДИНЕНИЕ " + ПредставлениеТипаВладельца+ "." + СправочникПапок.Имя + " КАК СправочникПапок
	|		ПО (СправочникФайлов.ВладелецФайла = СправочникПапок.Ссылка)
	|ГДЕ
	|	ТИПЗНАЧЕНИЯ(СправочникФайлов.ВладелецФайла) = &ТипВладельца";
	
	МассивУсловий = Новый Массив;
	Если МассивИсключений.Количество() > 0 Тогда
		МассивУсловий.Добавить("НЕ СправочникПапок.Ссылка В ИЕРАРХИИ (&МассивИсключений)"); // @query-part
	КонецЕсли;
	Если ЭлементИсключение <> Неопределено Тогда
		МассивУсловий.Добавить("СправочникПапок.Ссылка В ИЕРАРХИИ (&ЭлементИсключение)"); // @query-part
	КонецЕсли;
	
	УсловиеОтборПоПапкам = "";
	Если МассивУсловий.Количество() > 0 Тогда
		УсловиеОтборПоПапкам = СтрСоединить(МассивУсловий, " И ");
	КонецЕсли;
	
	Если Не ПустаяСтрока(УсловиеОтборПоПапкам) Тогда
		ТекстЗапроса = ТекстЗапроса + Символы.ПС + "И " + УсловиеОтборПоПапкам;
	КонецЕсли;
	
	ТекстЗапроса = ТекстЗапроса + "
	|
	|ОБЪЕДИНИТЬ ВСЕ
	|
	|ВЫБРАТЬ
	|	СправочникПапок.Ссылка,"; // @query-part
	
	ДобавитьДоступныеПоляОтбора(ТекстЗапроса, ВладелецФайла);
	
	ТекстЗапроса = ТекстЗапроса + "
	|	СправочникПапок.Ссылка,
	|	" + ?(ПредставлениеТипаВладельца = "Документ",
		"СправочникПапок.Представление", "СправочникПапок.Наименование") + ",
	|	"""",
	|	ЛОЖЬ,
	|	ЛОЖЬ,
	|	СправочникПапок.ПометкаУдаления,";
	
	Если ОбщегоНазначения.ЭтоСправочник(СправочникПапок) И СправочникПапок.Иерархический Тогда
		ТекстЗапроса = ТекстЗапроса + "
		|	ВЫБОР
		|		КОГДА СправочникПапок.Родитель = ЗНАЧЕНИЕ(Справочник." + СправочникПапок.Имя + ".ПустаяСсылка)
		|			ТОГДА НЕОПРЕДЕЛЕНО
		|		ИНАЧЕ СправочникПапок.Родитель
		|	КОНЕЦ,";
	Иначе
		ТекстЗапроса = ТекстЗапроса + "Неопределено,";
	КонецЕсли;
	
	ТекстЗапроса = ТекстЗапроса + "
	|	ИСТИНА,
	|	ИСТИНА,
	|	ЛОЖЬ,
	|	НЕОПРЕДЕЛЕНО,
	|	ЕСТЬNULL(СтатусыСинхронизацииФайловСОблачнымСервисом.Href, """"),
	|	"""",
	|	ЛОЖЬ,
	|	ДАТАВРЕМЯ(1, 1, 1, 0, 0, 0),
	|	"""",
	|	"""",
	|	"""",
	|	"""",
	|	"""",
	|	ЛОЖЬ,
	|	ЛОЖЬ,
	|	НЕОПРЕДЕЛЕНО,
	|	"""",
	|	" + ?(ЕстьВозможностьХранитьВерсии, "ИСТИНА", "ЛОЖЬ") + "
	|ИЗ
	|	" + ПредставлениеТипаВладельца + "." + СправочникПапок.Имя + " КАК СправочникПапок
	|		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.СтатусыСинхронизацииФайловСОблачнымСервисом КАК СтатусыСинхронизацииФайловСОблачнымСервисом
	|		ПО (СтатусыСинхронизацииФайловСОблачнымСервисом.Файл = СправочникПапок.Ссылка
	|			И СтатусыСинхронизацииФайловСОблачнымСервисом.УчетнаяЗапись = &УчетнаяЗапись)";
	
	
	Если Не ПустаяСтрока(УсловиеОтборПоПапкам) Тогда
		ТекстЗапроса = ТекстЗапроса + Символы.ПС + "ГДЕ " + УсловиеОтборПоПапкам;
	КонецЕсли;
	
	Возврат ТекстЗапроса;
	
КонецФункции

Функция ЭтоВладелецФайлов(ОбъектВладелец)
	
	МассивТиповФайлов = Метаданные.ОпределяемыеТипы.ВладелецПрисоединенныхФайлов.Тип.Типы();
	Возврат МассивТиповФайлов.Найти(ТипЗнч(ОбъектВладелец)) <> Неопределено;
	
КонецФункции

// Параметры:
//   ВладелецФайла - ОпределяемыйТип.ВладелецПрисоединенныхФайлов
//
Процедура ДобавитьДоступныеПоляОтбора(ТекстЗапроса, ВладелецФайла)
	
	ВсеСправочники = Справочники.ТипВсеСсылки();
	ВсеДокументы = Документы.ТипВсеСсылки();

	Если ВсеСправочники.СодержитТип(ТипЗнч(ВладелецФайла.ЗначениеПустойСсылки)) Тогда
		Справочник = Метаданные.Справочники[ВладелецФайла.Имя];
		ШаблонЗапроса = "СправочникПапок.%1 КАК %1,"; // @query-part
		Для Каждого Реквизит Из Справочник.Реквизиты Цикл
			ТекстЗапроса = ТекстЗапроса + Символы.ПС + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				ШаблонЗапроса, Реквизит.Имя);
		КонецЦикла;
	ИначеЕсли ВсеДокументы.СодержитТип(ТипЗнч(ВладелецФайла.ЗначениеПустойСсылки)) Тогда
		Документ = Метаданные.Документы[ВладелецФайла.Имя];
		ШаблонЗапроса = "СправочникПапок.%1,"; // @query-part
		ШаблонЗапросаДата = "РАЗНОСТЬДАТ(СправочникПапок.%1, &ТекущаяДата, ДЕНЬ) Как ДнейДоУдаленияОт%1,"; // @query-part
		Для Каждого Реквизит Из Документ.Реквизиты Цикл
			Если Реквизит.Тип.СодержитТип(Тип("Дата")) Тогда
				ТекстЗапроса = ТекстЗапроса + Символы.ПС + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					ШаблонЗапросаДата, Реквизит.Имя);
			КонецЕсли;
			ТекстЗапроса = ТекстЗапроса + Символы.ПС + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				ШаблонЗапроса, Реквизит.Имя);
		КонецЦикла;
	КонецЕсли;
	
КонецПроцедуры

// Проверяет, была ли ошибка при выполнении HTTP запроса, и вызывает соответствующее исключение.
Функция ПроверитьИсключениеHTTP1С(Ответ, АдресСервера)
	Результат = Новый Структура("Успешно, ТекстОшибки, КодОшибки");
	
	Если ЭтоКодСостоянияОшибки(Ответ.КодСостояния) Тогда
		
		ШаблонОшибки = НСтр("ru = 'Не удалось синхронизировать файл по адресу %2, т.к. сервер вернул HTTP код: %1. %3'");
		ИнформацияОбОшибке = Ответ.ПолучитьТелоКакСтроку();
		
		Результат.Успешно = Ложь;
		Результат.КодОшибки = Ответ.КодСостояния;
		Результат.ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонОшибки, 
			Ответ.КодСостояния, РаскодироватьСтроку(АдресСервера, СпособКодированияСтроки.URLВКодировкеURL), ИнформацияОбОшибке);
		
		Возврат Результат;
		
	КонецЕсли;
	
	Результат.Успешно = Истина;
	Возврат Результат;
	
КонецФункции

// Выполняет метод протокола webdav.
Функция ВыполнитьМетодWebdav(ИмяМетода, АдресФайлаHRef, СоответствиеЗаголовков, СтруктураОбмена, XMLЗапрос="", ТекстПротокола = Неопределено)

	СтруктураHref = СтруктураURIРаскодированная(АдресФайлаHRef);
	Соединение = СоздатьHTTPСоединениеWebdav(СтруктураHref, СтруктураОбмена, 20);
	HTTPЗапросWebdav = Новый HTTPЗапрос(СтруктураHref.ПутьНаСервере, СоответствиеЗаголовков);
	
	Если ЗначениеЗаполнено(XMLЗапрос) Тогда
		HTTPЗапросWebdav.УстановитьТелоИзСтроки(XMLЗапрос);
	КонецЕсли;
	
	Если ТекстПротокола <> Неопределено Тогда
		ТекстПротокола = ТекстПротокола + ?(ПустаяСтрока(ТекстПротокола), "", Символы.ПС)
			+ ИмяМетода + " " + АдресФайлаHRef + Символы.ПС + Символы.ПС + XMLЗапрос + Символы.ПС;
	КонецЕсли; 
	
	ВызватьМетодHTTP(СтруктураОбмена, Соединение, ИмяМетода, HTTPЗапросWebdav);
	
	Если ТекстПротокола <> Неопределено Тогда
		ТекстПротокола = ТекстПротокола + ?(ПустаяСтрока(ТекстПротокола), "", Символы.ПС) + "HTTP RESPONSE "
			+ СтруктураОбмена.Ответ.КодСостояния + Символы.ПС + Символы.ПС;
		Для каждого ЗаголовокОтвета Из СтруктураОбмена.Ответ.Заголовки Цикл
			ТекстПротокола = ТекстПротокола+ЗаголовокОтвета.Ключ + ": " + ЗаголовокОтвета.Значение + Символы.ПС;
		КонецЦикла; 
		ТекстПротокола = ТекстПротокола + Символы.ПС + СтруктураОбмена.Ответ.ПолучитьТелоКакСтроку() + Символы.ПС;
	КонецЕсли; 
	
	Возврат ПроверитьИсключениеHTTP1С(СтруктураОбмена.Ответ, АдресФайлаHRef);
	
КонецФункции

Процедура ВызватьМетодHTTP(СтруктураОбмена, Соединение, ИмяМетода, HTTPЗапросWebdav, ТекущаяПопытка = 1)
	
	СтруктураОбмена.Ответ = Соединение.ВызватьHTTPМетод(ИмяМетода, HTTPЗапросWebdav);
	Если СтруктураОбмена.Ответ.КодСостояния = 429
		И ТекущаяПопытка <= 5 Тогда
		
		ТекущееВремя = ТекущаяДатаСеанса();
		ВремяОжидания = ТекущееВремя + 2;
		Пока ТекущееВремя <= ВремяОжидания Цикл
			ТекущееВремя = ТекущаяДатаСеанса();
		КонецЦикла;
		
		ВызватьМетодHTTP(СтруктураОбмена, Соединение, ИмяМетода, HTTPЗапросWebdav, ТекущаяПопытка + 1)
		
	КонецЕсли;
	
КонецПроцедуры

// Обновляет уникальный служебный реквизит файла на сервере webdav.
Функция ОбновитьUID1CФайла(АдресФайлаHRef, UID1C, ПараметрыСинхронизации)
	
	ЗаголовкиHTTP                  = Новый Соответствие;
	ЗаголовкиHTTP["User-Agent"]   = "1C Enterprise 8.3";
	ЗаголовкиHTTP["Content-type"] = "text/xml";
	ЗаголовкиHTTP["Accept"]       = "text/xml";
	
	XMLЗапрос = "<?xml version=""1.0"" encoding=""utf-8""?>
				|<D:propertyupdate xmlns:D=""DAV:"" xmlns:U=""tsov.pro"">
				|  <D:set><D:prop>
				|    <U:UID1C>%1</U:UID1C>
				|  </D:prop></D:set>
				|</D:propertyupdate>";
	XMLЗапрос = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(XMLЗапрос, UID1C);
	
	Возврат ВыполнитьМетодWebdav("PROPPATCH", АдресФайлаHRef, ЗаголовкиHTTP, ПараметрыСинхронизации, XMLЗапрос);
	
КонецФункции

// Считывает уникальный служебный реквизит файла на сервере webdav.
Функция ПолучитьUID1C(АдресФайлаHRef, ПараметрыСинхронизации)

	ЗаголовкиHTTP                 = Новый Соответствие;
	ЗаголовкиHTTP["User-Agent"]   = "1C Enterprise 8.3";
	ЗаголовкиHTTP["Content-type"] = "text/xml";
	ЗаголовкиHTTP["Accept"]       = "text/xml";
	ЗаголовкиHTTP["Depth"]        = "0";
	
	Результат = ВыполнитьМетодWebdav("PROPFIND",АдресФайлаHRef,ЗаголовкиHTTP,ПараметрыСинхронизации,
					"<?xml version=""1.0"" encoding=""utf-8""?>
					|<D:propfind xmlns:D=""DAV:"" xmlns:U=""tsov.pro""><D:prop>
					|<U:UID1C />
					|</D:prop></D:propfind>");
	
	Если Не Результат.Успешно Тогда
		ЗаписатьВЖурналСобытийСинхронизацииФайлов(Результат.ТекстОшибки, ПараметрыСинхронизации.УчетнаяЗапись, 
			УровеньЖурналаРегистрации.Ошибка);
		Возврат "";
	КонецЕсли;

	КонтекстXml = ОпределитьКонтекстXML(ПараметрыСинхронизации.Ответ.ПолучитьТелоКакСтроку());
	НайденныеEtag = ВычислитьXPath("//*[local-name()='propstat'][contains(./*[local-name()='status'],'200')]/*[local-name()='prop']/*[local-name()='UID1C']",
		КонтекстXml).ПолучитьСледующий();
	Если НайденныеEtag <> Неопределено Тогда
		Возврат НайденныеEtag.ТекстовоеСодержимое;
	КонецЕсли;
	
	Возврат "";

КонецФункции

// Проверяет, поддерживает ли сервер webdav пользовательские свойства для файла.
Функция ПроверитьВозможностьUID1C(АдресФайлаHRef, UID1C, ПараметрыСинхронизации)
	
	ОбновитьUID1CФайла(АдресФайлаHRef, UID1C, ПараметрыСинхронизации);
	Возврат ЗначениеЗаполнено(ПолучитьUID1C(АдресФайлаHRef, ПараметрыСинхронизации));
	
КонецФункции

// Выполняет MCKOL на сервере webdav.
Функция ВызватьМетодMKCOL(АдресФайлаHRef, ПараметрыСинхронизации)

	ЗаголовкиHTTP               = Новый Соответствие;
	ЗаголовкиHTTP["User-Agent"] = "1C Enterprise 8.3";
	Возврат ВыполнитьМетодWebdav("MKCOL", АдресФайлаHRef, ЗаголовкиHTTP, ПараметрыСинхронизации);

КонецФункции

// Выполняет DELETE на сервере webdav.
Функция ВызватьМетодDELETE(АдресФайлаHRef, ПараметрыСинхронизации)
	
	HrefБезСлеша = ЗакончитьБезСлеша(АдресФайлаHRef);
	ЗаголовкиHTTP               = Новый Соответствие;
	ЗаголовкиHTTP["User-Agent"] = "1C Enterprise 8.3";
	Возврат ВыполнитьМетодWebdav("DELETE", HrefБезСлеша, ЗаголовкиHTTP, ПараметрыСинхронизации);
	
КонецФункции

// Получает Etag файла на сервере.
Функция ПолучитьEtag(АдресФайлаHRef, ПараметрыСинхронизации)
	
	ЗаголовкиHTTP                 = Новый Соответствие;
	ЗаголовкиHTTP["User-Agent"]   = "1C Enterprise 8.3";
	ЗаголовкиHTTP["Content-type"] = "text/xml";
	ЗаголовкиHTTP["Accept"]       = "text/xml";
	ЗаголовкиHTTP["Depth"]        = "0";
	
	Результат = ВыполнитьМетодWebdav("PROPFIND",АдресФайлаHRef,ЗаголовкиHTTP,ПараметрыСинхронизации,
					"<?xml version=""1.0"" encoding=""utf-8""?>
					|<D:propfind xmlns:D=""DAV:""><D:prop>
					|<D:getetag />
					|</D:prop></D:propfind>");
	
	Если Не Результат.Успешно Тогда
		ЗаписатьВЖурналСобытийСинхронизацииФайлов(Результат.ТекстОшибки, ПараметрыСинхронизации.УчетнаяЗапись, 
			УровеньЖурналаРегистрации.Ошибка);
		Возврат "";
	КонецЕсли;
	
	КонтекстXml = ОпределитьКонтекстXML(ПараметрыСинхронизации.Ответ.ПолучитьТелоКакСтроку());
	НайденныеEtag = ВычислитьXPath("//*[local-name()='propstat'][contains(./*[local-name()='status'],'200')]/*[local-name()='prop']/*[local-name()='getetag']",
		КонтекстXml).ПолучитьСледующий();
	Если НайденныеEtag <> Неопределено Тогда
		Возврат НайденныеEtag.ТекстовоеСодержимое;
	КонецЕсли;
	
	Возврат "";
	
КонецФункции

// Инициализирует объект HTTPСоединение.
Функция СоздатьHTTPСоединениеWebdav(СтруктураHref, ПараметрыСинхронизации, Таймаут)
	
	ИнтернетПрокси = Неопределено;
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ПолучениеФайловИзИнтернета") Тогда
		МодульПолучениеФайловИзИнтернета = ОбщегоНазначения.ОбщийМодуль("ПолучениеФайловИзИнтернета");
		ИнтернетПрокси = МодульПолучениеФайловИзИнтернета.ПолучитьПрокси("https");
	КонецЕсли;
	
	ЗащищенноеСоединение = Неопределено;
	Если СтруктураHref.Схема = "https" Тогда 
		ЗащищенноеСоединение = ОбщегоНазначенияКлиентСервер.НовоеЗащищенноеСоединение();
	КонецЕсли;
	
	Если Не ЗначениеЗаполнено(СтруктураHref.Порт) Тогда
		Результат = Новый HTTPСоединение(
			СтруктураHref.Хост,
			,
			ПараметрыСинхронизации.Логин,
			ПараметрыСинхронизации.Пароль,
			ИнтернетПрокси,
			Таймаут,
			ЗащищенноеСоединение);
	Иначе
		Результат = Новый HTTPСоединение(
			СтруктураHref.Хост,
			СтруктураHref.Порт,
			ПараметрыСинхронизации.Логин,
			ПараметрыСинхронизации.Пароль,
			ИнтернетПрокси,
			Таймаут,
			ЗащищенноеСоединение);
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции

// Вызывает метод GET на сервере webdav и возвращает адрес загруженного файла во временном хранилище.
Функция ВызватьМетодGET(АдресФайлаHRef, ИдентификаторEtag, ПараметрыСинхронизации, ДатаМодификацииФайла = Неопределено, ДлинаФайла = Неопределено)

	Результат = Новый Структура("Успешно, АдресВременныхДанных, ТекстОшибки");
	
	СтруктураHref = СтруктураURIРаскодированная(АдресФайлаHRef);
	
	Таймаут = ?(ДлинаФайла <> Неопределено, РассчитатьТаймаут(ДлинаФайла), 43200);
	Соединение = СоздатьHTTPСоединениеWebdav(СтруктураHref, ПараметрыСинхронизации, Таймаут);
	
	ЗаголовкиHTTP               = Новый Соответствие;
	ЗаголовкиHTTP["User-Agent"] = "1C Enterprise 8.3";
	ЗаголовкиHTTP["Accept"]     = "application/octet-stream";
	
	HTTPЗапросWebdav = Новый HTTPЗапрос(СтруктураHref.ПутьНаСервере, ЗаголовкиHTTP);
	ПараметрыСинхронизации.Ответ = Соединение.Получить(HTTPЗапросWebdav);
	
	Результат = ПроверитьИсключениеHTTP1С(ПараметрыСинхронизации.Ответ, АдресФайлаHRef);
	Если НЕ Результат.Успешно Тогда
		Возврат Результат;
	КонецЕсли;
	
	ФайлСДвоичнымиДанными = ПараметрыСинхронизации.Ответ.ПолучитьТелоКакДвоичныеДанные(); // ДвоичныеДанные
	
	// АПК:216-выкл Идентификаторы внешнего сервиса содержат кириллицу и латиницу.
	HTTPЗаголовки = СтандартныеПодсистемыСервер.HTTPЗаголовкиВНижнийРегистр(ПараметрыСинхронизации.Ответ.Заголовки);
	ИдентификаторEtag = ?(HTTPЗаголовки["идентификаторetag"] = Неопределено, "", HTTPЗаголовки["идентификаторetag"]);
	ДатаМодификацииФайла = ?(HTTPЗаголовки["last-modified"] = Неопределено, ТекущаяУниверсальнаяДата(), 
		ОбщегоНазначенияКлиентСервер.ДатаRFC1123(HTTPЗаголовки["last-modified"]));
	ДлинаФайла = ФайлСДвоичнымиДанными.Размер();
	// АПК:216-вкл
	
	ЭтоПодписьИлиЗашифрованныеДанные = ЭтоПодписьИлиЗашифрованныеДанные(СтруктураHref.ПутьНаСервере, ФайлСДвоичнымиДанными);
	Результат.Вставить("ЭтоПодпись", ЭтоПодписьИлиЗашифрованныеДанные.Подпись);
	Результат.Вставить("ЭтоЗашифрованныеДанные", ЭтоПодписьИлиЗашифрованныеДанные.ЗашифрованныеДанные);
	
	АдресВременныхДанных = ПоместитьВоВременноеХранилище(ФайлСДвоичнымиДанными);
	Результат.Вставить("АдресЗагруженногоФайла", АдресВременныхДанных);
	
	Возврат Результат;

КонецФункции

// Помещает файл на сервер webdav с помощью метода PUT и возвращает присвоенный etag в переменную.
Функция ВызватьМетодPUT(АдресФайлаHRef, ФайлСсылка, ПараметрыСинхронизации, ЭтоФайл)
	
	Если ТипЗнч(ФайлСсылка) = Тип("ДвоичныеДанные") Тогда
		ФайлСДвоичнымиДанными = ФайлСсылка;
	Иначе
		ФайлСДвоичнымиДанными = РаботаСФайлами.ДвоичныеДанныеФайла(ФайлСсылка);
	КонецЕсли;
	
	СтруктураHref = СтруктураURIРаскодированная(АдресФайлаHRef);
	
	Таймаут = РассчитатьТаймаут(ФайлСДвоичнымиДанными.Размер());
	Соединение = СоздатьHTTPСоединениеWebdav(СтруктураHref, ПараметрыСинхронизации, Таймаут);
	
	ЗаголовкиHTTP = Новый Соответствие;
	ЗаголовкиHTTP["User-Agent"]   = "1C Enterprise 8.3";
	ЗаголовкиHTTP["Content-Type"] = "application/octet-stream";
	
	HTTPЗапросWebdav = Новый HTTPЗапрос(СтруктураHref.ПутьНаСервере, ЗаголовкиHTTP);
	HTTPЗапросWebdav.УстановитьТелоИзДвоичныхДанных(ФайлСДвоичнымиДанными);
	ПараметрыСинхронизации.Ответ = Соединение.Записать(HTTPЗапросWebdav);
	ПроверитьИсключениеHTTP1С(ПараметрыСинхронизации.Ответ, АдресФайлаHRef);
	Возврат ПолучитьEtag(АдресФайлаHRef,ПараметрыСинхронизации);
	
КонецФункции

// Загружает файл с сервера с созданием новой версии.
Функция ЗагрузитьФайлССервера(ПараметрыФайла, ЭтоФайл = Неопределено)
	
	ИмяФайла                 = ПараметрыФайла.ИмяФайла;
	АдресФайла               = ПараметрыФайла.Href;
	ИдентификаторEtag        = ПараметрыФайла.Etag;
	ДатаМодификацииФайла     = ПараметрыФайла.ДатаМодификацииФайла;
	ДлинаФайла               = ПараметрыФайла.ДлинаФайла;
	ОбъектВладелец           = ПараметрыФайла.ОбъектВладелец;
	СсылкаСуществующегоФайла = ПараметрыФайла.СсылкаСуществующегоФайла;
	ПараметрыСинхронизации   = ПараметрыФайла.ПараметрыСинхронизации;
	
	ТекстСобытия = НСтр("ru = 'Загрузка файла с сервера: %1'");
	ЗаписатьВЖурналСобытийСинхронизацииФайлов(СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ТекстСобытия, ПараметрыФайла.ИмяФайла), 
		ПараметрыСинхронизации.УчетнаяЗапись);
	
	РезультатЗагрузки = ВызватьМетодGET(АдресФайла, ИдентификаторEtag, ПараметрыСинхронизации, ДатаМодификацииФайла, ДлинаФайла);
	
	Если ЭтоФайл = Неопределено Тогда
		ЭтоФайл = ЭтоВладелецФайлов(ПараметрыФайла.ОбъектВладелец);
	КонецЕсли;
	
	Зашифрован = ЭтоФайл = Истина И РезультатЗагрузки.ЭтоЗашифрованныеДанные;
	
	Если Зашифрован И СтрЗаканчиваетсяНа(ИмяФайла, ".p7m") Тогда
		ИмяФайла = НаименованиеЗашифрованногоФайла(ИмяФайла);
		ПараметрыФайла.ИмяФайла = ИмяФайла;
	КонецЕсли;
	
	Если РезультатЗагрузки.Успешно И РезультатЗагрузки.АдресЗагруженногоФайла <> Неопределено Тогда
		
		АдресЗагруженногоФайла = РезультатЗагрузки.АдресЗагруженногоФайла;
		
		СтруктураИмениФайла = Новый Файл(ИмяФайла);
		
		Если СсылкаСуществующегоФайла = Неопределено Тогда
			
			ПараметрыДобавляемогоФайла = РаботаСФайлами.ПараметрыДобавленияФайла();
			Если СтрНачинаетсяС(ОбъектВладелец.Метаданные().ПолноеИмя(), "Справочник") И ОбъектВладелец.ЭтоГруппа Тогда
				ПараметрыДобавляемогоФайла.ГруппаФайлов = ОбъектВладелец;
				ПараметрыДобавляемогоФайла.ВладелецФайлов = ОбъектВладелец.ВладелецФайла;
			Иначе
				ПараметрыДобавляемогоФайла.ВладелецФайлов = ОбъектВладелец;
			КонецЕсли;
			
			ПараметрыДобавляемогоФайла.Автор = ПараметрыСинхронизации.АвторФайлов;
			ПараметрыДобавляемогоФайла.ИмяБезРасширения = СтруктураИмениФайла.ИмяБезРасширения;
			ПараметрыДобавляемогоФайла.РасширениеБезТочки = ОбщегоНазначенияКлиентСервер.РасширениеБезТочки(СтруктураИмениФайла.Расширение);
			ПараметрыДобавляемогоФайла.ВремяИзмененияУниверсальное = ДатаМодификацииФайла;
			ПараметрыДобавляемогоФайла.Вставить("Зашифрован", Зашифрован);
			ПараметрыФайла.Зашифрован = Зашифрован;
			
			НовыйФайл = РаботаСФайлами.ДобавитьФайл(ПараметрыДобавляемогоФайла, АдресЗагруженногоФайла);
			
			ЗанятьФайлДляРедактированияСервер(НовыйФайл, ПараметрыСинхронизации.АвторФайлов);
			
		Иначе
			
			РеквизитыФайла = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(СсылкаСуществующегоФайла, "ХранитьВерсии, ТекущаяВерсия");
			Режим = ?(РеквизитыФайла.ХранитьВерсии, "ФайлСВерсией", "Файл");
			
			СведенияОФайле = РаботаСФайламиКлиентСервер.СведенияОФайле(Режим);

			СведенияОФайле.ИмяБезРасширения              = СтруктураИмениФайла.ИмяБезРасширения;
			СведенияОФайле.АдресВременногоХранилищаФайла = АдресЗагруженногоФайла;
			СведенияОФайле.РасширениеБезТочки            = ОбщегоНазначенияКлиентСервер.РасширениеБезТочки(
				СтруктураИмениФайла.Расширение);
			СведенияОФайле.ВремяИзмененияУниверсальное   = ДатаМодификацииФайла;
			СведенияОФайле.Зашифрован                    = Зашифрован;

			Если СведенияОФайле.ХранитьВерсии Тогда
				СведенияОФайле.НоваяВерсияАвтор          = ПараметрыСинхронизации.АвторФайлов;
			КонецЕсли;
			
			Если ПараметрыФайла.Зашифрован <> Зашифрован Тогда
				
				ДанныеДляЗаписиНаСервере = Новый Структура;
				ДанныеДляЗаписиНаСервере.Вставить("АдресВременногоХранилища", АдресЗагруженногоФайла);
				ДанныеДляЗаписиНаСервере.Вставить("ВерсияСсылка", РеквизитыФайла.ТекущаяВерсия);
				ДанныеДляЗаписиНаСервере.Вставить("АдресВременногоХранилищаТекста", "");
				ДанныеДляЗаписиНаСервере.Вставить("АдресФайла", "");
				
				ПараметрыЗаписиИнформацииОШифровании = ПараметрыЗаписиИнформацииОШифровании();
				ПараметрыЗаписиИнформацииОШифровании.Зашифровать = Зашифрован;
				ПараметрыЗаписиИнформацииОШифровании.МассивДанныхДляЗанесенияВБазу.Добавить(ДанныеДляЗаписиНаСервере);
				ПараметрыЗаписиИнформацииОШифровании.СведенияОФайле = СведенияОФайле;
				
				ЗаписатьИнформациюОШифровании(СсылкаСуществующегоФайла, ПараметрыЗаписиИнформацииОШифровании);
			Иначе
				РаботаСФайламиСлужебныйВызовСервера.СохранитьИзмененияФайла(СсылкаСуществующегоФайла, СведенияОФайле, Истина, "", "", Ложь);
			КонецЕсли;
			
			НовыйФайл = СсылкаСуществующегоФайла;
			
		КонецЕсли;
		
		ФайлUID1C = Строка(НовыйФайл.УникальныйИдентификатор());
		ОбновитьUID1CФайла(АдресФайла, ФайлUID1C, ПараметрыСинхронизации);
		
		ЗапомнитьСерверныеДанныеСсылки(НовыйФайл, АдресФайла, ИдентификаторEtag, ЭтоФайл, ОбъектВладелец, Ложь, ПараметрыСинхронизации.УчетнаяЗапись);
		
		ТекстСообщения = НСтр("ru = 'Загружен файл из облачного сервиса: ""%1""'");
		СтатусДляЖурналаРегистрации = УровеньЖурналаРегистрации.Информация;
	Иначе
		ТекстСообщения = НСтр("ru = 'Не удалось загрузить файл ""%1"" из облачного сервиса по причине:'") + " " + Символы.ПС + РезультатЗагрузки.ТекстОшибки;
		СтатусДляЖурналаРегистрации = УровеньЖурналаРегистрации.Ошибка;
	КонецЕсли;
	
	ЗаписатьВЖурналСобытийСинхронизацииФайлов(СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ТекстСообщения, ИмяФайла), 
		ПараметрыСинхронизации.УчетнаяЗапись, СтатусДляЖурналаРегистрации);
	
	Возврат НовыйФайл;

КонецФункции

Процедура ЗаписатьВЖурналСобытийСинхронизацииФайлов(ТекстСообщения, УчетнаяЗапись, УстанавливаемыйУровеньЖурналаРегистрации = Неопределено)

	Если УстанавливаемыйУровеньЖурналаРегистрации = Неопределено Тогда
		УстанавливаемыйУровеньЖурналаРегистрации = УровеньЖурналаРегистрации.Информация;
	КонецЕсли;

	ЗаписьЖурналаРегистрации(СобытиеЖурналаРегистрацииСинхронизация(),
					УстанавливаемыйУровеньЖурналаРегистрации,,
					УчетнаяЗапись,
					ТекстСообщения);
	
КонецПроцедуры

Функция СобытиеЖурналаРегистрацииСинхронизация()
	
	Возврат НСтр("ru = 'Файлы.Синхронизация с облачным сервисом'", ОбщегоНазначения.КодОсновногоЯзыка());
	
КонецФункции

// Считывает основные данные о состоянии каталога на сервере. Используется для проверки подключения.
Процедура ПрочитатьПараметрыКаталога(РезультатПроверки, HttpАдрес, СтруктураОбмена)

	СтруктураАдресаHTTP = СтруктураURIРаскодированная(HttpАдрес);
	АдресСервера = КодироватьURIПоСтруктуре(СтруктураАдресаHTTP);
	
	Попытка
		// получаем каталог
		ЗаголовкиHTTP = Новый Соответствие;
		ЗаголовкиHTTP["User-Agent"]   = "1C Enterprise 8.3";
		ЗаголовкиHTTP["Content-type"] = "text/xml";
		ЗаголовкиHTTP["Accept"]       = "text/xml";
		ЗаголовкиHTTP["Depth"]        = "0";
		
		Результат = ВыполнитьМетодWebdav("PROPFIND", АдресСервера, ЗаголовкиHTTP, СтруктураОбмена,
						"<?xml version=""1.0"" encoding=""utf-8""?>
						|<D:propfind xmlns:D=""DAV:"" xmlns:U=""tsov.pro""><D:prop>
						|<D:quota-used-bytes /><D:quota-available-bytes />
						|</D:prop></D:propfind>",
						РезультатПроверки.РезультатПротокол);
		
		Если Результат.Успешно = Ложь Тогда
			
			РезультатСозданияКорневойПапки = ВызватьМетодMKCOL(АдресСервера, СтруктураОбмена);
			Если РезультатСозданияКорневойПапки.Успешно = Истина Тогда
				
				Результат = ВыполнитьМетодWebdav("PROPFIND", АдресСервера, ЗаголовкиHTTP, СтруктураОбмена,
								"<?xml version=""1.0"" encoding=""utf-8""?>
								|<D:propfind xmlns:D=""DAV:"" xmlns:U=""tsov.pro""><D:prop>
								|<D:quota-used-bytes /><D:quota-available-bytes />
								|</D:prop></D:propfind>",
								РезультатПроверки.РезультатПротокол);
								
			КонецЕсли;
			
		КонецЕсли;
		
		Если Не Результат.Успешно Тогда
			
			РезультатПроверки.Отказ = Истина;
			РезультатПроверки.КодОшибки = Результат.КодОшибки;
			РезультатПроверки.РезультатТекст = Результат.ТекстОшибки;
			ЗаписатьВЖурналСобытийСинхронизацииФайлов(Результат.ТекстОшибки, СтруктураОбмена.УчетнаяЗапись, 
				УровеньЖурналаРегистрации.Ошибка);
			Возврат;
		
		КонецЕсли;
		
		КонтекстДокументаXML = ОпределитьКонтекстXML(СтруктураОбмена.Ответ.ПолучитьТелоКакСтроку());
		РезультатXPath = ВычислитьXPath("//*[local-name()='response']",КонтекстДокументаXML);
		НайденныйResponse = РезультатXPath.ПолучитьСледующий();
		
		Пока НайденныйResponse <> Неопределено Цикл
			
			НайденныйPropstat = ВычислитьXPath("./*[local-name()='propstat'][contains(./*[local-name()='status'],'200')]/*[local-name()='prop']", 
				КонтекстДокументаXML, НайденныйResponse).ПолучитьСледующий();
			Если НайденныйPropstat<>Неопределено Тогда
				Для каждого ДочернийУзелPropstat Из НайденныйPropstat.ДочерниеУзлы Цикл
					Если ДочернийУзелPropstat.ЛокальноеИмя = "quota-available-bytes" Тогда
						Попытка
							РазмерВМегабайтах = Окр(Число(ДочернийУзелPropstat.ТекстовоеСодержимое)/1024/1024, 1);
						Исключение
							РазмерВМегабайтах = 0;
						КонецПопытки;
						
						ИнформацияОСвободномМесте = НСтр("ru = 'Свободное место : %1 Мб'");
						
						РезультатПроверки.РезультатТекст = РезультатПроверки.РезультатТекст 
							+ ?(ПустаяСтрока(РезультатПроверки.РезультатТекст), "", Символы.ПС)
							+ СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ИнформацияОСвободномМесте, РазмерВМегабайтах);
					ИначеЕсли ДочернийУзелPropstat.ЛокальноеИмя = "quota-used-bytes" Тогда
						Попытка
							РазмерВМегабайтах = Окр(Число(ДочернийУзелPropstat.ТекстовоеСодержимое)/1024/1024, 1);
						Исключение
							РазмерВМегабайтах = 0;
						КонецПопытки;
						
						ИнформацияОЗанятомМесте = НСтр("ru = 'Занято : %1 Мб'");
						
						РезультатПроверки.РезультатТекст = РезультатПроверки.РезультатТекст 
							+ ?(ПустаяСтрока(РезультатПроверки.РезультатТекст), "", Символы.ПС)
							+ СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ИнформацияОЗанятомМесте, РазмерВМегабайтах);
					КонецЕсли; 
				КонецЦикла; 
			КонецЕсли; 
			
			НайденныйResponse = РезультатXPath.ПолучитьСледующий();
			
		КонецЦикла;
	
	Исключение
		ОписаниеОшибки = ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
		РезультатПроверки.РезультатТекст = РезультатПроверки.РезультатТекст + ?(ПустаяСтрока(РезультатПроверки.РезультатТекст), "", Символы.ПС) + ОписаниеОшибки;
		ЗаписатьВЖурналСобытийСинхронизацииФайлов(ОписаниеОшибки, СтруктураОбмена.УчетнаяЗапись, УровеньЖурналаРегистрации.Ошибка);
		РезультатПроверки.Отказ = Истина;
	КонецПопытки; 
	
КонецПроцедуры

Процедура ПроверитьВозможностьРазмещенияФайлов(РезультатПроверки, HttpАдрес, ПараметрыСинхронизации)
	АдресФайлаHRef = ЗакончитьБезСлеша(HttpАдрес) + НачатьСлешем("test.txt");
	СтруктураHref = СтруктураURIРаскодированная(АдресФайлаHRef);
	ФайлСДвоичнымиДанными = ПолучитьДвоичныеДанныеИзСтроки("test");
	
	Таймаут = РассчитатьТаймаут(ФайлСДвоичнымиДанными.Размер());
	Соединение = СоздатьHTTPСоединениеWebdav(СтруктураHref, ПараметрыСинхронизации, Таймаут);
	
	ЗаголовкиHTTP = Новый Соответствие;
	ЗаголовкиHTTP["User-Agent"]   = "1C Enterprise 8.3";
	ЗаголовкиHTTP["Content-Type"] = "application/octet-stream";
	
	HTTPЗапросWebdav = Новый HTTPЗапрос(СтруктураHref.ПутьНаСервере, ЗаголовкиHTTP);
	HTTPЗапросWebdav.УстановитьТелоИзДвоичныхДанных(ФайлСДвоичнымиДанными);
	ПараметрыСинхронизации.Ответ = Соединение.Записать(HTTPЗапросWebdav);
	РезультатОтправкиФайла = ПроверитьИсключениеHTTP1С(ПараметрыСинхронизации.Ответ, АдресФайлаHRef);
	
	РезультатПроверки.РезультатПротокол = РезультатПроверки.РезультатПротокол + ?(РезультатПроверки.РезультатПротокол = "", "", Символы.ПС)
		+ "PUT" + " " + АдресФайлаHRef + Символы.ПС;
	
	Если РезультатОтправкиФайла.Успешно Тогда
		РезультатПроверки.РезультатПротокол = РезультатПроверки.РезультатПротокол + ?(ПустаяСтрока(РезультатПроверки.РезультатПротокол), "", Символы.ПС) + "HTTP RESPONSE "
			+ ПараметрыСинхронизации.Ответ.КодСостояния + Символы.ПС + Символы.ПС;
		Для каждого ЗаголовокОтвета Из ПараметрыСинхронизации.Ответ.Заголовки Цикл
			РезультатПроверки.РезультатПротокол = РезультатПроверки.РезультатПротокол+ЗаголовокОтвета.Ключ + ": " + ЗаголовокОтвета.Значение + Символы.ПС;
		КонецЦикла; 
	Иначе
		ЗаписатьВЖурналСобытийСинхронизацииФайлов(РезультатПроверки.РезультатТекст, ПараметрыСинхронизации.УчетнаяЗапись, УровеньЖурналаРегистрации.Ошибка);
	    РезультатПроверки.Отказ = Истина;
		РезультатПроверки.КодОшибки = РезультатПроверки.КодОшибки;
		Возврат;
	КонецЕсли;
	
	ЗаголовкиHTTP["Content-type"] = "text/xml";
	ЗаголовкиHTTP["Accept"]       = "text/xml";
	
	XMLЗапрос = "<?xml version=""1.0"" encoding=""utf-8""?>
				|<D:propertyupdate xmlns:D=""DAV:"" xmlns:U=""tsov.pro"">
				|  <D:set><D:prop>
				|    <U:UID1C>%1</U:UID1C>
				|  </D:prop></D:set>
				|</D:propertyupdate>";
	XMLЗапрос = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(XMLЗапрос, Новый УникальныйИдентификатор);
	
	РезультатУстановкиСвойства = ВыполнитьМетодWebdav("PROPPATCH", АдресФайлаHRef, ЗаголовкиHTTP, ПараметрыСинхронизации, XMLЗапрос, РезультатПроверки.РезультатПротокол);
	
	Если Не РезультатУстановкиСвойства.Успешно Тогда
		ЗаписатьВЖурналСобытийСинхронизацииФайлов(РезультатУстановкиСвойства.ТекстОшибки, ПараметрыСинхронизации.УчетнаяЗапись, УровеньЖурналаРегистрации.Ошибка);
		РезультатПроверки.КодОшибки = РезультатУстановкиСвойства.КодОшибки;
	    РезультатПроверки.Отказ = Истина;
		РезультатПроверки.РезультатТекст = РезультатПроверки.РезультатТекст + ?(ПустаяСтрока(РезультатПроверки.РезультатТекст), "", Символы.ПС) + РезультатУстановкиСвойства.ТекстОшибки;
		Возврат;
	Иначе
		КонтекстXml = ОпределитьКонтекстXML(ПараметрыСинхронизации.Ответ.ПолучитьТелоКакСтроку());
		УзелОтвета = ВычислитьXPath("//*[local-name()='propstat']/*[local-name()='status']", КонтекстXml).ПолучитьСледующий();
		КодОтветаУстановкиСвойства = Число(СтрРазделить(УзелОтвета.ПервыйДочерний.ТекстовоеСодержимое, " ")[1]);
		Если ЭтоКодСостоянияОшибки(КодОтветаУстановкиСвойства) Тогда
			РезультатПроверки.Отказ = Истина;
			РезультатПроверки.КодОшибки = 10000+КодОтветаУстановкиСвойства;
			
			ШаблонОшибки = НСтр("ru = 'Не удалось установить свойство файла по адресу %2, т.к. сервер вернул HTTP код: %1. %3'");
			ИнформацияОбОшибке = ПараметрыСинхронизации.Ответ.ПолучитьТелоКакСтроку();
		
			РезультатПроверки.РезультатТекст = РезультатПроверки.РезультатТекст + ?(ПустаяСтрока(РезультатПроверки.РезультатТекст), "", Символы.ПС) 
				+ СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонОшибки, КодОтветаУстановкиСвойства, 
				РаскодироватьСтроку(АдресФайлаHRef, СпособКодированияСтроки.URLВКодировкеURL), ИнформацияОбОшибке);
			
			РезультатПроверки.РезультатПротокол = РезультатПроверки.РезультатПротокол + ?(ПустаяСтрока(РезультатПроверки.РезультатПротокол), "", Символы.ПС) + "HTTP RESPONSE "
			+ УзелОтвета.ПервыйДочерний.ТекстовоеСодержимое + Символы.ПС + Символы.ПС;
			Для каждого ЗаголовокОтвета Из ПараметрыСинхронизации.Ответ.Заголовки Цикл
				РезультатПроверки.РезультатПротокол = РезультатПроверки.РезультатПротокол+ЗаголовокОтвета.Ключ + ": " + ЗаголовокОтвета.Значение + Символы.ПС;
			КонецЦикла; 
			РезультатПроверки.РезультатПротокол = РезультатПроверки.РезультатПротокол + Символы.ПС + ПараметрыСинхронизации.Ответ.ПолучитьТелоКакСтроку() + Символы.ПС;
		КонецЕсли;
	КонецЕсли;
	
	РезультатУдаленияФайла = ВызватьМетодDELETE(АдресФайлаHRef, ПараметрыСинхронизации);
	
	Если Не РезультатУдаленияФайла.Успешно Тогда
		ЗаписатьВЖурналСобытийСинхронизацииФайлов(РезультатУдаленияФайла.ТекстОшибки, ПараметрыСинхронизации.УчетнаяЗапись, УровеньЖурналаРегистрации.Ошибка);
		РезультатПроверки.КодОшибки = РезультатУдаленияФайла.КодОшибки;
	    РезультатПроверки.Отказ = Истина;
		РезультатПроверки.РезультатТекст = РезультатПроверки.РезультатТекст + ?(ПустаяСтрока(РезультатПроверки.РезультатТекст), "", Символы.ПС) + РезультатУдаленияФайла.ТекстОшибки;
	КонецЕсли;
КонецПроцедуры

// Возвращает структуру URI
Функция СтруктураURIРаскодированная(Знач СтрокаURI)
	
	СтрокаURI = СокрЛП(СтрокаURI);
	
	// Схема
	Схема = "";
	Позиция = СтрНайти(СтрокаURI, "://");
	Если Позиция > 0 Тогда
		Схема = НРег(Лев(СтрокаURI, Позиция - 1));
		СтрокаURI = Сред(СтрокаURI, Позиция + 3);
	КонецЕсли;

	// Строка соединения и путь на сервере.
	СтрокаСоединения = СтрокаURI;
	ПутьНаСервере = "";
	Позиция = СтрНайти(СтрокаСоединения, "/");
	Если Позиция > 0 Тогда
		// Включая первый слэш
		ПутьНаСервере = Сред(СтрокаСоединения, Позиция);
		СтрокаСоединения = Лев(СтрокаСоединения, Позиция - 1);
	КонецЕсли;
		
	// Информация пользователя и имя сервера.
	СтрокаАвторизации = "";
	ИмяСервера = СтрокаСоединения;
	Позиция = СтрНайти(СтрокаСоединения, "@");
	Если Позиция > 0 Тогда
		СтрокаАвторизации = Лев(СтрокаСоединения, Позиция - 1);
		ИмяСервера = Сред(СтрокаСоединения, Позиция + 1);
	КонецЕсли;
	
	// Логин и пароль
	Логин = СтрокаАвторизации;
	Пароль = "";
	Позиция = СтрНайти(СтрокаАвторизации, ":");
	Если Позиция > 0 Тогда
		Логин = Лев(СтрокаАвторизации, Позиция - 1);
		Пароль = Сред(СтрокаАвторизации, Позиция + 1);
	КонецЕсли;
	
	// Хост и порт
	Хост = ИмяСервера;
	Порт = "";
	Позиция = СтрНайти(ИмяСервера, ":");
	Если Позиция > 0 Тогда
		Хост = Лев(ИмяСервера, Позиция - 1);
		Порт = Сред(ИмяСервера, Позиция + 1);
	КонецЕсли;
	
	Результат = Новый Структура;
	Результат.Вставить("Схема", НРег(Схема));
	Результат.Вставить("Логин", Логин);
	Результат.Вставить("Пароль", Пароль);
	Результат.Вставить("ИмяСервера", НРег(ИмяСервера));
	Результат.Вставить("Хост", НРег(Хост));
	Результат.Вставить("Порт", ?(ПустаяСтрока(Порт), Неопределено, Число(Порт)));
	Результат.Вставить("ПутьНаСервере", РаскодироватьСтроку(ЗакончитьБезСлеша(ПутьНаСервере),СпособКодированияСтроки.URLВКодировкеURL)); 
	
	// Путь на сервере всегда будет с начальным, но без конечного слэша, универсально для файлов и папок.
	Возврат Результат; 
	
КонецФункции

// Возвращает URI, составленный из структуры.
Функция КодироватьURIПоСтруктуре(Знач СтруктураURI, ВключаяПутьНаСервере = Истина)
	Результат = "";
	
	// Протокол
	Если Не ПустаяСтрока(СтруктураURI.Схема) Тогда
		Результат = Результат + СтруктураURI.Схема + "://";
	КонецЕсли;
	
	// Авторизация
	Если Не ПустаяСтрока(СтруктураURI.Логин) Тогда
		Результат = Результат + СтруктураURI.Логин + ":" + СтруктураURI.Пароль + "@";
	КонецЕсли;
		
	// Все остальное
	Результат = Результат + СтруктураURI.Хост;
	Если ЗначениеЗаполнено(СтруктураURI.Порт) Тогда
		Результат = Результат + ":" + ?(ТипЗнч(СтруктураURI.Порт) = Тип("Число"), Формат(СтруктураURI.Порт, "ЧГ=0"), СтруктураURI.Порт);
	КонецЕсли;
	
	Результат = Результат + ?(ВключаяПутьНаСервере, ЗакончитьБезСлеша(СтруктураURI.ПутьНаСервере), "");
	
	// Всегда без конечного слеша
	Возврат Результат; 
	
КонецФункции

// Возвращает строку, гарантированно начинающуюся прямым слешем.
Функция НачатьСлешем(Знач ИсходнаяСтрока)
	Возврат ?(Лев(ИсходнаяСтрока,1)="/", ИсходнаяСтрока, "/"+ИсходнаяСтрока);
КонецФункции 

// Возвращает строку, гарантированно заканчивающуюся без прямого слэша.
Функция ЗакончитьБезСлеша(Знач ИсходнаяСтрока)
	Возврат ?(Прав(ИсходнаяСтрока,1)="/", Лев(ИсходнаяСтрока, СтрДлина(ИсходнаяСтрока)-1), ИсходнаяСтрока);
КонецФункции

// Возвращает результат сравнения путей двух URI, вне зависимости от наличия начального и конечного прямого слеша,
// кодирования спецсимволов, а также адреса сервера.
//
Функция ЭтоОдинаковыеПутиURI(URI1, URI2, ЧувствительноКРегистру = Истина, ИгнорироватьРасширениеЗашифрованного = Ложь)
	
	// Гарантирует идентичность вне зависимости от слэшей и кодирования.
	СтруктураURI1 = СтруктураURIРаскодированная(URI1); 
	СтруктураURI2 = СтруктураURIРаскодированная(URI2);
	Если НЕ ЧувствительноКРегистру Тогда
		СтруктураURI1.ПутьНаСервере = НРег(СтруктураURI1.ПутьНаСервере);
		СтруктураURI2.ПутьНаСервере = НРег(СтруктураURI2.ПутьНаСервере);
	КонецЕсли;
	
	КодированныйURI1 = КодироватьURIПоСтруктуре(СтруктураURI1,Истина);
	КодированныйURI2 = КодироватьURIПоСтруктуре(СтруктураURI2,Истина);
	
	Результат = КодированныйURI1 = КодированныйURI2;
	
	Если Не Результат И ИгнорироватьРасширениеЗашифрованного Тогда
		Если СтрЗаканчиваетсяНа(КодированныйURI1, ".p7m") Тогда
			КодированныйURI1 = НаименованиеЗашифрованногоФайла(КодированныйURI1);
		КонецЕсли;
		Если СтрЗаканчиваетсяНа(КодированныйURI2, ".p7m") Тогда
			КодированныйURI2 = НаименованиеЗашифрованногоФайла(КодированныйURI2);
		КонецЕсли;
		Результат = КодированныйURI1 = КодированныйURI2;
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции

// Возвращает имя файла по адресу файла.
Функция ИмяФайлаИзАдреса(АдресФайла)

	URIСтрока = ЗакончитьБезСлеша(АдресФайла);
	ДлинаURI = СтрДлина(URIСтрока);
	
	// Находим последний слэш, после него будет имя файла.
	Для Индекс = 1 По ДлинаURI Цикл
		СимволURI = Сред(URIСтрока,ДлинаURI - Индекс + 1, 1);
		Если СимволURI = "/" Тогда
			Возврат РаскодироватьСтроку(Сред(URIСтрока,ДлинаURI - Индекс + 2), СпособКодированияСтроки.КодировкаURL);
		КонецЕсли;
	КонецЦикла;
	
	Возврат РаскодироватьСтроку(URIСтрока, СпособКодированияСтроки.КодировкаURL);

КонецФункции

// Сохраняет данные о Href и Etag файла или папки в базе данных.
Процедура ЗапомнитьСерверныеДанныеСсылки(
		Ссылка,
		АдресФайлаHRef,
		ИдентификаторEtag,
		ЭтоФайл,
		ВладелецФайла,
		ЭтоПапка,
		УчетнаяЗапись = Неопределено)

	ЗаписьРегистра = РегистрыСведений.СтатусыСинхронизацииФайловСОблачнымСервисом.СоздатьМенеджерЗаписи();
	ЗаписьРегистра.Файл                        = Ссылка;
	ЗаписьРегистра.Href                        = АдресФайлаHRef;
	ЗаписьРегистра.Etag                        = ИдентификаторEtag;
	ЗаписьРегистра.УникальныйИдентификатор1С   = ?(ТипЗнч(Ссылка) = Тип("Строка"), "", Ссылка.УникальныйИдентификатор());
	ЗаписьРегистра.ЭтоФайл                     = ЭтоФайл;
	ЗаписьРегистра.ЭтоВладелецФайла            = ЭтоПапка;
	ЗаписьРегистра.ВладелецФайла               = ВладелецФайла;
	ЗаписьРегистра.УчетнаяЗапись               = УчетнаяЗапись;
	ЗаписьРегистра.Синхронизирован             = Ложь;
	ЗаписьРегистра.ДатаСинхронизацииНачало     = ТекущаяДатаСеанса();
	ЗаписьРегистра.ДатаСинхронизацииЗавершение = ТекущаяДатаСеанса() + 1800; // 30 минут
	ЗаписьРегистра.НомерСеанса                 = НомерСеансаИнформационнойБазы();
	ЗаписьРегистра.Записать(Истина);
	
КонецПроцедуры

// Сохраняет данные о Href и Etag файла или папки в базе данных.
Процедура УстановитьСтатусСинхронизацииФайла(СведенияОФайле, УчетнаяЗапись)

	ЗаписьРегистра = РегистрыСведений.СтатусыСинхронизацииФайловСОблачнымСервисом.СоздатьМенеджерЗаписи();
	ЗаписьРегистра.Файл                        = СведенияОФайле.ФайлСсылка;
	ЗаписьРегистра.Href                        = СведенияОФайле.ToHref;
	ЗаписьРегистра.Etag                        = СведенияОФайле.ToEtag;
	ЗаписьРегистра.УникальныйИдентификатор1С   = СведенияОФайле.ФайлСсылка.УникальныйИдентификатор();
	ЗаписьРегистра.ЭтоФайл                     = СведенияОФайле.ЭтоФайл;
	ЗаписьРегистра.ЭтоВладелецФайла            = СведенияОФайле.ЭтоПапка;
	ЗаписьРегистра.ВладелецФайла               = СведенияОФайле.Родитель;
	ЗаписьРегистра.Синхронизирован             = СведенияОФайле.Обработан;
	ЗаписьРегистра.ДатаСинхронизацииНачало     = ТекущаяДатаСеанса();
	ЗаписьРегистра.ДатаСинхронизацииЗавершение = ТекущаяДатаСеанса();
	ЗаписьРегистра.НомерСеанса                 = НомерСеансаИнформационнойБазы();
	
	ЗаписьРегистра.УчетнаяЗапись               = УчетнаяЗапись;
	
	ЗаписьРегистра.Записать(Истина);
	
КонецПроцедуры

// Удаляет данные о Href и Etag файла или папки в базе данных.
Процедура УдалитьСтатусСинхронизацииФайла(ФайлСсылка, УчетнаяЗапись)

	НаборРегистра = РегистрыСведений.СтатусыСинхронизацииФайловСОблачнымСервисом.СоздатьНаборЗаписей();
	НаборРегистра.Отбор.Файл.Установить(ФайлСсылка);
	НаборРегистра.Отбор.УчетнаяЗапись.Установить(УчетнаяЗапись);
	НаборРегистра.Записать(Истина);

КонецПроцедуры

Процедура ЗаблокироватьСтатусСинхронизацииФайла(Блокировка, ФайлСсылка, УчетнаяЗапись)

	ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.СтатусыСинхронизацииФайловСОблачнымСервисом");
	ЭлементБлокировки.УстановитьЗначение("Файл", ФайлСсылка);
	ЭлементБлокировки.УстановитьЗначение("УчетнаяЗапись", УчетнаяЗапись);
	
КонецПроцедуры

// Определяет контекст xml
Функция ОпределитьКонтекстXML(ТекстXML)
	
	ЧтениеXMLТекста = Новый ЧтениеXML;
	ЧтениеXMLТекста.УстановитьСтроку(ТекстXML);
	ПостроительDOMДляXML = Новый ПостроительDOM;
	ДокументDOMДляXML = ПостроительDOMДляXML.Прочитать(ЧтениеXMLТекста);
	РазыменовательИменДляXML = Новый РазыменовательПространствИменDOM(ДокументDOMДляXML);
	Возврат Новый Структура("ДокументDOM,РазыменовательDOM", ДокументDOMДляXML, РазыменовательИменДляXML); 
	
КонецФункции

// Вычисляет выражение xpath для контекста xml.
Функция ВычислитьXPath(Выражение, Контекст, УзелКонтекста = Неопределено)
	
	Возврат Контекст.ДокументDOM.ВычислитьВыражениеXPath(Выражение,?(УзелКонтекста=Неопределено,Контекст.ДокументDOM,УзелКонтекста),Контекст.РазыменовательDOM);
	
КонецФункции

// Возвращает Href, рассчитанный для строки из таблицы файлов, методом поиска всех родителей.
Функция РассчитатьHref(СтрокаФайлов,ТаблицаФайлов)
	
	// Рекурсивно собираем наименования.
	НайденыСтрокиФайлов = ТаблицаФайлов.Найти(СтрокаФайлов.Родитель,"ФайлСсылка");
	Если НайденыСтрокиФайлов = Неопределено Тогда
		Возврат ?(ЗначениеЗаполнено(СтрокаФайлов.Наименование),
			ОбщегоНазначенияКлиентСервер.ЗаменитьНедопустимыеСимволыВИмениФайла(СтрокаФайлов.Наименование, "-") + "/","");
	Иначе
		Возврат РассчитатьHref(НайденыСтрокиФайлов,ТаблицаФайлов)
			+ ОбщегоНазначенияКлиентСервер.ЗаменитьНедопустимыеСимволыВИмениФайла(СтрокаФайлов.Наименование, "-") +"/";
	КонецЕсли;
	
КонецФункции

// Возвращает строку таблицы файлов по URI, при этом, учитывается возможное различное написание URI 
// (например, закодированное, относительное или абсолютное и т.п.).
//
Функция НайтиСтрокуПоURI(ИскомоеURI, ТаблицаСURI, КолонкаURI, ИгнорироватьРасширениеЗашифрованного = Ложь)

	Для каждого СтрокаТаблицы Из ТаблицаСURI Цикл
		Если ЭтоОдинаковыеПутиURI(ИскомоеURI,СтрокаТаблицы[КолонкаURI],,ИгнорироватьРасширениеЗашифрованного) Тогда
			Возврат СтрокаТаблицы;
		КонецЕсли; 
	КонецЦикла; 
	
	Возврат Неопределено;
	
КонецФункции

// Вычисляется уровень строки файлов, рекурсивным алгоритмом.
Функция РекурсивноУровень(СтрокаФайлов,ТаблицаФайлов)
	
	// Равен уровню в базе или на сервере, в зависимости от того, где он меньше.
	НайденыСтрокиФайлов = ТаблицаФайлов.НайтиСтроки(Новый Структура("ФайлСсылка", СтрокаФайлов.Родитель));
	КоличествоПрибавления = ?(НайденыСтрокиФайлов.Количество() = 0, 0, 1);
	Для каждого НайденнаяСтрокаФайлов Из НайденыСтрокиФайлов Цикл
		КоличествоПрибавления = КоличествоПрибавления + РекурсивноУровень(НайденнаяСтрокаФайлов,ТаблицаФайлов);
	КонецЦикла;
	
	Возврат КоличествоПрибавления;
	
КонецФункции

// Вычисляется уровень файла на сервере webdav, рекурсивным алгоритмом.
Функция РекурсивноУровеньНаСервере(СтрокаФайлов,ТаблицаФайлов) 
	
	НайденыСтрокиФайлов = ТаблицаФайлов.НайтиСтроки(Новый Структура("ФайлСсылка", СтрокаФайлов.РодительСервер));
	КоличествоПрибавления = ?(НайденыСтрокиФайлов.Количество() = 0, 0, 1);
	Для каждого НайденнаяСтрокаФайлов Из НайденыСтрокиФайлов Цикл
		КоличествоПрибавления = КоличествоПрибавления + РекурсивноУровеньНаСервере(НайденнаяСтрокаФайлов, ТаблицаФайлов);
	КонецЦикла;
	
	Возврат КоличествоПрибавления;
	
КонецФункции

// Вычисляет уровни всех строк таблицы файлов.
Процедура ВычислитьУровеньРекурсивно(ТаблицаФайлов)
	ТаблицаФайлов.Индексы.Добавить("ФайлСсылка");
	Для каждого СтрокаФайлов Из ТаблицаФайлов Цикл
		
		Если НЕ ЗначениеЗаполнено(СтрокаФайлов.ФайлСсылка) Тогда
			Продолжить;
		КонецЕсли;
		
		// Равен уровню в базе или на сервере, в зависимости от того, где он меньше.
		УровеньВБазе    = РекурсивноУровень(СтрокаФайлов, ТаблицаФайлов);
		УровеньНаСервере = РекурсивноУровеньНаСервере(СтрокаФайлов, ТаблицаФайлов);
		Если УровеньНаСервере = 0 Тогда
			СтрокаФайлов.Уровень            = УровеньВБазе;
			СтрокаФайлов.РодительСортировка = СтрокаФайлов.Родитель;
		Иначе
			Если УровеньВБазе <= УровеньНаСервере Тогда
				СтрокаФайлов.Уровень            = УровеньВБазе;
				СтрокаФайлов.РодительСортировка = СтрокаФайлов.Родитель;
			Иначе
				СтрокаФайлов.Уровень            = УровеньНаСервере;
				СтрокаФайлов.РодительСортировка = СтрокаФайлов.РодительСервер;
			КонецЕсли;
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

// Рекурсивно загружает перечень файлов с сервера в таблицу файлов.
Процедура СкачатьДеревоФайловРекурсивно(ТекущиеСтрокиДереваФайлов, HttpАдрес, ПараметрыСинхронизации, Отказ=Ложь)

	СтруктураАдресаHTTP   = СтруктураURIРаскодированная(HttpАдрес);
	АдресОблачногоСервиса = КодироватьURIПоСтруктуре(СтруктураАдресаHTTP, Ложь);
	АдресСервера          = КодироватьURIПоСтруктуре(СтруктураАдресаHTTP);
	
	Попытка
		// Получаем каталог
		ЗаголовкиHTTP = Новый Соответствие;
		ЗаголовкиHTTP["User-Agent"] = "1C Enterprise 8.3";
		ЗаголовкиHTTP["Content-type"] = "text/xml";
		ЗаголовкиHTTP["Accept"] = "text/xml";
		ЗаголовкиHTTP["Depth"] = "1";
		
		Результат = ВыполнитьМетодWebdav("PROPFIND", АдресСервера, ЗаголовкиHTTP, ПараметрыСинхронизации,
						"<?xml version=""1.0"" encoding=""utf-8""?>
						|<D:propfind xmlns:D=""DAV:"" xmlns:U=""tsov.pro""><D:prop>
						|<D:getetag /><U:UID1C /><D:resourcetype />
						|<D:getlastmodified /><D:getcontentlength />
						|</D:prop></D:propfind>");
		
		Если Результат.Успешно = Ложь Тогда
			ЗаписатьВЖурналСобытийСинхронизацииФайлов(Результат.ТекстОшибки, ПараметрыСинхронизации.УчетнаяЗапись, 
				УровеньЖурналаРегистрации.Ошибка);
			Возврат;
		КонецЕсли;
		
		КонтекстДокументаXML = ОпределитьКонтекстXML(ПараметрыСинхронизации.Ответ.ПолучитьТелоКакСтроку());
		
		РезультатXPath = ВычислитьXPath("//*[local-name()='response']", КонтекстДокументаXML);
		
		НайденныйResponse = РезультатXPath.ПолучитьСледующий();
		
		Пока НайденныйResponse <> Неопределено Цикл
			
			// Href есть всегда, иначе это критическая ошибка.
			НайденныйHref = ВычислитьXPath("./*[local-name()='href']", КонтекстДокументаXML, НайденныйResponse).ПолучитьСледующий();
			Если НайденныйHref = Неопределено Тогда
				ТекстОшибки = НСтр("ru = 'Ошибка ответа от сервера: не найден HREF в %1'");
				ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ТекстОшибки, АдресСервера);
			КонецЕсли; 
			
			ТекстHref = ЗакончитьБезСлеша(НачатьСлешем(НайденныйHref.ТекстовоеСодержимое));
			
			Если ЭтоОдинаковыеПутиURI(АдресОблачногоСервиса + ТекстHref, АдресСервера) Тогда
				НайденныйResponse = РезультатXPath.ПолучитьСледующий();
				Продолжить;
			КонецЕсли; 
			
			НоваяСтрокаДереваФайлов = ТекущиеСтрокиДереваФайлов.Добавить();
			// Всегда закодирована
			НоваяСтрокаДереваФайлов.Href = АдресОблачногоСервиса + РаскодироватьСтроку(ТекстHref, СпособКодированияСтроки.КодировкаURL);
			НоваяСтрокаДереваФайлов.ИмяФайла = ИмяФайлаИзАдреса(НоваяСтрокаДереваФайлов.Href);
			НоваяСтрокаДереваФайлов.Etag = "";
			НоваяСтрокаДереваФайлов.UID1C = "";
			НоваяСтрокаДереваФайлов.ЭтоПапка = Неопределено;
			
			НайденныйPropstat = ВычислитьXPath("./*[local-name()='propstat'][contains(./*[local-name()='status'],'200')]/*[local-name()='prop']", КонтекстДокументаXML, НайденныйResponse).ПолучитьСледующий();
			
			Если НайденныйPropstat <> Неопределено Тогда
				Для каждого ДочернийУзелPropstat Из НайденныйPropstat.ДочерниеУзлы Цикл
					Если ДочернийУзелPropstat.ЛокальноеИмя = "resourcetype" Тогда
						НоваяСтрокаДереваФайлов.ЭтоПапка = ВычислитьXPath("./*[local-name()='collection']", КонтекстДокументаXML, ДочернийУзелPropstat).ПолучитьСледующий() <> Неопределено;
					ИначеЕсли ДочернийУзелPropstat.ЛокальноеИмя = "UID1C" Тогда
						НоваяСтрокаДереваФайлов.UID1C = ДочернийУзелPropstat.ТекстовоеСодержимое;
						НоваяСтрокаДереваФайлов.UID1CНеПоддерживается = Ложь;
					ИначеЕсли ДочернийУзелPropstat.ЛокальноеИмя = "getetag" Тогда
						НоваяСтрокаДереваФайлов.Etag = ДочернийУзелPropstat.ТекстовоеСодержимое;
					ИначеЕсли ДочернийУзелPropstat.ЛокальноеИмя = "getlastmodified" Тогда
						НоваяСтрокаДереваФайлов.ДатаМодификации = ОбщегоНазначенияКлиентСервер.ДатаRFC1123(ДочернийУзелPropstat.ТекстовоеСодержимое);//UTC
					ИначеЕсли ДочернийУзелPropstat.ЛокальноеИмя = "getcontentlength" Тогда
						НоваяСтрокаДереваФайлов.Длина = Число("0" + СтрЗаменить(ДочернийУзелPropstat.ТекстовоеСодержимое," ",""));
					КонецЕсли;
				КонецЦикла;
			КонецЕсли;
			
			НайденныйPropstat = ВычислитьXPath("./*[local-name()='propstat'][contains(./*[local-name()='status'],'404')]/*[local-name()='prop']", КонтекстДокументаXML, НайденныйResponse).ПолучитьСледующий();
			
			Если НайденныйPropstat <> Неопределено Тогда
				Для каждого ДочернийУзелPropstat Из НайденныйPropstat.ДочерниеУзлы Цикл
					Если ДочернийУзелPropstat.ИмяУзла = "UID1C" Тогда
						НоваяСтрокаДереваФайлов.UID1CНеПоддерживается = Истина;
					КонецЕсли;
				КонецЦикла;
			КонецЕсли;
			
			// Если не было UID, пробуем получить его отдельно, это нужно, например, для owncloud.
			Если НоваяСтрокаДереваФайлов.UID1CНеПоддерживается = Ложь И НЕ ЗначениеЗаполнено(НоваяСтрокаДереваФайлов.UID1C) Тогда
				НоваяСтрокаДереваФайлов.UID1C = ПолучитьUID1C(НоваяСтрокаДереваФайлов.Href, ПараметрыСинхронизации);
			КонецЕсли;
			
			НайденныйResponse = РезультатXPath.ПолучитьСледующий();
			
		КонецЦикла;
	
	Исключение
		ЗаписатьВЖурналСобытийСинхронизацииФайлов(ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()), 
			ПараметрыСинхронизации.УчетнаяЗапись, УровеньЖурналаРегистрации.Ошибка);
		Отказ = Истина;
	КонецПопытки;
	
	Для каждого СтрокаДереваФайлов Из ТекущиеСтрокиДереваФайлов Цикл
		Если СтрокаДереваФайлов.ЭтоПапка = Истина Тогда
			СкачатьДеревоФайловРекурсивно(СтрокаДереваФайлов.Строки, СтрокаДереваФайлов.Href, ПараметрыСинхронизации, Отказ);
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

// Загружает с сервера webdav новые папки и файлы, которых еще нет в базе, и отражает их в таблице файлов.
Процедура ЗагрузитьНовыеПрисоединенныеФайлы(СтрокиДереваФайлов, ТаблицаФайлов, ПараметрыСинхронизации, Подписи, ОбъектВладелец = Неопределено)
	
	ВладельцыФайлов = ВладельцыФайловПоУникальнымИдентификаторам(СтрокиДереваФайлов);
	Для каждого СтрокаДереваФайлов Из СтрокиДереваФайлов Цикл
		
		Если СтрокаДереваФайлов.ЭтоПапка Тогда
			ТекущаяПапкаФайлов = Неопределено;
			Если Не ПустаяСтрока(СтрокаДереваФайлов.UID1C) Тогда
				СтрокаТаблицы = ВладельцыФайлов.Найти(Новый УникальныйИдентификатор(СтрокаДереваФайлов.UID1C), "УникальныйИдентификатор1С");
				Если СтрокаТаблицы <> Неопределено Тогда
					ТекущаяПапкаФайлов = СтрокаТаблицы.Файл; // ОпределяемыйТип.ВладелецФайлов
				КонецЕсли;
			КонецЕсли;
			
			Если (ТекущаяПапкаФайлов = Неопределено) И (ТаблицаФайлов.Найти(СтрокаДереваФайлов.Href, "Href") = Неопределено) Тогда
				
				// Это новая папка на сервере.
				// Если папка лежит в корне каталога обмена или корне типа синхронизируемого объекта метаданных, она не относится к объекту-владельцу.
				// Такие папки игнорируются.
				Если ОбъектВладелец = Неопределено
					Или ТипЗнч(ОбъектВладелец) = Тип("СправочникСсылка.ИдентификаторыОбъектовМетаданных") Тогда
					Продолжить;
				КонецЕсли;
				
				// Проверим возможность хранения UID1C, и если нельзя - то папка не грузится.
				Если НЕ ПроверитьВозможностьUID1C(СтрокаДереваФайлов.Href, Строка(Новый УникальныйИдентификатор), ПараметрыСинхронизации) Тогда
					ТекстСобытия = НСтр("ru = 'Невозможно сохранение дополнительных свойств файла, он не будет загружен: %1'");
					ЗаписатьВЖурналСобытийСинхронизацииФайлов(СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
							ТекстСобытия, СтрокаДереваФайлов.ИмяФайла), 
						ПараметрыСинхронизации.УчетнаяЗапись, УровеньЖурналаРегистрации.Ошибка);
					Продолжить;
				КонецЕсли;
				
				Попытка
					
					ТекущаяПапкаФайлов = РаботаСФайламиСлужебныйВызовСервера.СоздатьПапкуФайлов(СтрокаДереваФайлов.ИмяФайла, 
						ОбъектВладелец, ПараметрыСинхронизации.АвторФайлов);
					
					СтрокаДереваФайлов.UID1C = Строка(ТекущаяПапкаФайлов.УникальныйИдентификатор());
					ОбновитьUID1CФайла(СтрокаДереваФайлов.Href, СтрокаДереваФайлов.UID1C, ПараметрыСинхронизации);
					
					НоваяСтрокаТаблицыФайлов                    = ТаблицаФайлов.Добавить();
					НоваяСтрокаТаблицыФайлов.ФайлСсылка         = ТекущаяПапкаФайлов;
					НоваяСтрокаТаблицыФайлов.ПометкаУдаления    = Ложь;
					НоваяСтрокаТаблицыФайлов.Родитель           = ОбъектВладелец;
					НоваяСтрокаТаблицыФайлов.ЭтоПапка           = Истина;
					НоваяСтрокаТаблицыФайлов.UID1C              = СтрокаДереваФайлов.UID1C;
					НоваяСтрокаТаблицыФайлов.ЕстьВБазе          = Истина;
					НоваяСтрокаТаблицыФайлов.ЕстьНаСервере      = Истина;
					НоваяСтрокаТаблицыФайлов.ИзмененНаСервере   = Ложь;
					НоваяСтрокаТаблицыФайлов.Изменения          = ТекущаяПапкаФайлов;
					НоваяСтрокаТаблицыФайлов.Href               = "";
					НоваяСтрокаТаблицыФайлов.Etag               = "";
					НоваяСтрокаТаблицыФайлов.ToHref             = СтрокаДереваФайлов.Href;
					НоваяСтрокаТаблицыФайлов.ToEtag             = СтрокаДереваФайлов.Etag;
					НоваяСтрокаТаблицыФайлов.РодительСервер     = ОбъектВладелец;
					НоваяСтрокаТаблицыФайлов.Наименование       = СтрокаДереваФайлов.ИмяФайла;
					НоваяСтрокаТаблицыФайлов.НаименованиеСервер = СтрокаДереваФайлов.ИмяФайла;
					НоваяСтрокаТаблицыФайлов.Обработан          = Истина;
					НоваяСтрокаТаблицыФайлов.ЭтоФайл            = Истина;
					
					ЗапомнитьСерверныеДанныеСсылки(
						НоваяСтрокаТаблицыФайлов.ФайлСсылка,
						НоваяСтрокаТаблицыФайлов.ToHref,
						НоваяСтрокаТаблицыФайлов.ToEtag,
						НоваяСтрокаТаблицыФайлов.ЭтоФайл,
						НоваяСтрокаТаблицыФайлов.Родитель,
						НоваяСтрокаТаблицыФайлов.ЭтоПапка,
						ПараметрыСинхронизации.УчетнаяЗапись);
					
					ТекстСобытия = НСтр("ru = 'Загружена папка с сервера:  %1'");
					ЗаписатьВЖурналСобытийСинхронизацииФайлов(СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
							ТекстСобытия, НоваяСтрокаТаблицыФайлов.НаименованиеСервер), 
						ПараметрыСинхронизации.УчетнаяЗапись);
					
				Исключение
					ЗаписатьВЖурналСобытийСинхронизацииФайлов(ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()),
						ПараметрыСинхронизации.УчетнаяЗапись, УровеньЖурналаРегистрации.Ошибка);
				КонецПопытки;

				Продолжить;
			КонецЕсли;
				
			Если Не ЗначениеЗаполнено(ТекущаяПапкаФайлов) Тогда
				
				// В РС СтатусыСинхронизацииФайловСОблачнымСервисом нет данных о папке, ищем ее в файлах.
				СтараяСтрокаТаблицыФайлов = ТаблицаФайлов.Найти(СтрокаДереваФайлов.Href, "Href");
				Если СтараяСтрокаТаблицыФайлов = Неопределено Тогда
					СтараяСтрокаТаблицыФайлов = ТаблицаФайлов.Найти(РаскодироватьСтроку(СтрокаДереваФайлов.Href,
						СпособКодированияСтроки.URLВКодировкеURL), "Href");
				КонецЕсли;
				
				Если СтараяСтрокаТаблицыФайлов = Неопределено Тогда
					ТекстСобытия = НСтр("ru = 'Пропущена синхронизация папки %1. Папка отсутствует на сервере.'");
					ЗаписатьВЖурналСобытийСинхронизацииФайлов(СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
						ТекстСобытия, РаскодироватьСтроку(СтрокаДереваФайлов.Href, СпособКодированияСтроки.URLВКодировкеURL)),
						ПараметрыСинхронизации.УчетнаяЗапись, УровеньЖурналаРегистрации.Ошибка);
					Продолжить;
				КонецЕсли;
				
				// Папка найдена по пути Href, теперь по UID1C ищем запись в РС СтатусыСинхронизацииФайловСОблачнымСервисом
				// @skip-check query-in-loop - Редкий запрос в цикле при пакетной загрузке данных.
				ТекущаяПапкаФайлов = ВладелецФайлаПоУникальномуИдентификатору(СтараяСтрокаТаблицыФайлов.UID1C);	
				Если Не ЗначениеЗаполнено(ТекущаяПапкаФайлов) Тогда
					ТекстСобытия = НСтр("ru = 'Невозможно синхронизировать папку %1.
						|Идентификатор папки %2 отсутствует в сведениях о синхронизации файлов.'");
					ЗаписатьВЖурналСобытийСинхронизацииФайлов(СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
						ТекстСобытия, РаскодироватьСтроку(СтрокаДереваФайлов.Href, СпособКодированияСтроки.URLВКодировкеURL),
							СтараяСтрокаТаблицыФайлов.UID1C),
						ПараметрыСинхронизации.УчетнаяЗапись, УровеньЖурналаРегистрации.Ошибка);
					Продолжить;
				КонецЕсли;
				
			Иначе
				СтараяСтрокаТаблицыФайлов = ТаблицаФайлов.Найти(ТекущаяПапкаФайлов.Ссылка, "ФайлСсылка");
			КонецЕсли;
			
			// Обновим ToHref
			Если СтараяСтрокаТаблицыФайлов <> Неопределено Тогда
				СтараяСтрокаТаблицыФайлов.ToHref             = СтрокаДереваФайлов.Href;
				СтараяСтрокаТаблицыФайлов.ToEtag             = СтрокаДереваФайлов.Etag;
				СтараяСтрокаТаблицыФайлов.РодительСервер     = ОбъектВладелец;
				СтараяСтрокаТаблицыФайлов.НаименованиеСервер = СтрокаДереваФайлов.ИмяФайла;
				СтараяСтрокаТаблицыФайлов.ЕстьНаСервере      = Истина;
				СтараяСтрокаТаблицыФайлов.ИзмененНаСервере   = НЕ ЭтоОдинаковыеПутиURI(СтараяСтрокаТаблицыФайлов.ToHref,СтараяСтрокаТаблицыФайлов.Href);
			КонецЕсли;
			
			// @skip-check query-in-loop - Рекурсивный алгоритм обработки дерева.
			ЗагрузитьНовыеПрисоединенныеФайлы(СтрокаДереваФайлов.Строки, ТаблицаФайлов, ПараметрыСинхронизации, Подписи, ТекущаяПапкаФайлов.Ссылка);
			Продолжить;
			
		КонецЕсли;

		// Это файл
		Если ОбъектВладелец = Неопределено
			Или ТипЗнч(ОбъектВладелец) = Тип("СправочникСсылка.ИдентификаторыОбъектовМетаданных") Тогда
			// Файл пропущен, т.к. добавлен пользователем в некорректную папку, у которой отсутствует владелец.
			Продолжить;
		КонецЕсли;
		
		ТекущийФайл = НайтиСтрокуПоURI(СтрокаДереваФайлов.Href, ТаблицаФайлов, "Href", Истина);
		
		ЭтоПодписьИлиЗашифрованныеДанные = ЭтоПодписьИлиЗашифрованныеДанные(СтрокаДереваФайлов.Href);
		
		Если ТекущийФайл = Неопределено И ЭтоПодписьИлиЗашифрованныеДанные.Подпись Тогда
			ДобавитьПодписьИзСервиса(Подписи, СтрокаДереваФайлов);
			Продолжить;
		КонецЕсли;
		
		Если (ТекущийФайл = Неопределено) ИЛИ (ТаблицаФайлов.Найти(ТекущийФайл.ФайлСсылка ,"ФайлСсылка") = Неопределено) Тогда
			// Это новый файл на сервере - грузим его.
			Если НЕ ПроверитьВозможностьUID1C(СтрокаДереваФайлов.Href, Строка(Новый УникальныйИдентификатор), ПараметрыСинхронизации) Тогда
				ТекстСобытия = НСтр("ru = 'Невозможно сохранение дополнительных свойств файла, он не будет загружен: %1'");
				ЗаписатьВЖурналСобытийСинхронизацииФайлов(СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					ТекстСобытия, СтрокаДереваФайлов.ИмяФайла), ПараметрыСинхронизации.УчетнаяЗапись, УровеньЖурналаРегистрации.Ошибка);
				Продолжить;
			КонецЕсли;
			
			Попытка
				
				ПараметрыФайла = Новый Структура;
				ПараметрыФайла.Вставить("ИмяФайла",                 СтрокаДереваФайлов.ИмяФайла);
				ПараметрыФайла.Вставить("Href",                     СтрокаДереваФайлов.Href);
				ПараметрыФайла.Вставить("Etag",                     СтрокаДереваФайлов.Etag);
				ПараметрыФайла.Вставить("ДатаМодификацииФайла",     СтрокаДереваФайлов.ДатаМодификации);
				ПараметрыФайла.Вставить("ДлинаФайла",               СтрокаДереваФайлов.Длина);
				ПараметрыФайла.Вставить("ДляПользователя",          ПараметрыСинхронизации.АвторФайлов);
				ПараметрыФайла.Вставить("ОбъектВладелец",           ОбъектВладелец);
				ПараметрыФайла.Вставить("СсылкаСуществующегоФайла", Неопределено);
				ПараметрыФайла.Вставить("ПараметрыСинхронизации",   ПараметрыСинхронизации);
				ПараметрыФайла.Вставить("Зашифрован",               Ложь);
				
				СсылкаСуществующегоФайла = ЗагрузитьФайлССервера(ПараметрыФайла);
				
				СтрокаДереваФайлов.UID1C = Строка(СсылкаСуществующегоФайла.Ссылка.УникальныйИдентификатор());
				
				НоваяСтрокаТаблицыФайлов                    = ТаблицаФайлов.Добавить();
				НоваяСтрокаТаблицыФайлов.ФайлСсылка         = СсылкаСуществующегоФайла;
				НоваяСтрокаТаблицыФайлов.ПометкаУдаления    = Ложь;
				НоваяСтрокаТаблицыФайлов.Родитель           = ОбъектВладелец;
				НоваяСтрокаТаблицыФайлов.ЭтоПапка           = Ложь;
				НоваяСтрокаТаблицыФайлов.UID1C              = СтрокаДереваФайлов.UID1C;
				НоваяСтрокаТаблицыФайлов.ЕстьВБазе          = Ложь;
				НоваяСтрокаТаблицыФайлов.ЕстьНаСервере      = Истина;
				НоваяСтрокаТаблицыФайлов.ИзмененНаСервере   = Ложь;
				НоваяСтрокаТаблицыФайлов.Href               = "";
				НоваяСтрокаТаблицыФайлов.Etag               = "";
				НоваяСтрокаТаблицыФайлов.ToHref             = СтрокаДереваФайлов.Href;
				НоваяСтрокаТаблицыФайлов.ToEtag             = СтрокаДереваФайлов.Etag;
				НоваяСтрокаТаблицыФайлов.РодительСервер     = ОбъектВладелец;
				НоваяСтрокаТаблицыФайлов.Наименование       = ПараметрыФайла.ИмяФайла;
				НоваяСтрокаТаблицыФайлов.НаименованиеСервер = ПараметрыФайла.ИмяФайла;
				НоваяСтрокаТаблицыФайлов.Обработан          = Истина;
				НоваяСтрокаТаблицыФайлов.ЭтоФайл            = Истина;
				НоваяСтрокаТаблицыФайлов.Зашифрован         = ПараметрыФайла.Зашифрован;
				
			Исключение
				ЗаписатьВЖурналСобытийСинхронизацииФайлов(ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()), 
					ПараметрыСинхронизации.УчетнаяЗапись, УровеньЖурналаРегистрации.Ошибка);
			КонецПопытки;
			
		Иначе
			// Обновим ToHref
			СтараяСтрокаТаблицыФайлов                    = ТаблицаФайлов.Найти(ТекущийФайл.ФайлСсылка,"ФайлСсылка");
			СтараяСтрокаТаблицыФайлов.ToHref             = СтрокаДереваФайлов.Href;
			СтараяСтрокаТаблицыФайлов.ToEtag             = СтрокаДереваФайлов.Etag;
			СтараяСтрокаТаблицыФайлов.РодительСервер     = ОбъектВладелец;
			
			Если СтрЗаканчиваетсяНа(СтрокаДереваФайлов.ИмяФайла, ".p7m") Тогда
				СтараяСтрокаТаблицыФайлов.НаименованиеСервер = НаименованиеЗашифрованногоФайла(СтрокаДереваФайлов.ИмяФайла);
				СтараяСтрокаТаблицыФайлов.ЗашифрованНаСервере = Истина;
			Иначе
				СтараяСтрокаТаблицыФайлов.НаименованиеСервер = СтрокаДереваФайлов.ИмяФайла;
			КонецЕсли;
			
			СтараяСтрокаТаблицыФайлов.ЕстьНаСервере      = Истина;
			СтараяСтрокаТаблицыФайлов.ИзмененНаСервере   = НЕ ЭтоОдинаковыеПутиURI(СтараяСтрокаТаблицыФайлов.ToHref, СтараяСтрокаТаблицыФайлов.Href);
		КонецЕсли;
		
	КонецЦикла;
	
КонецПроцедуры

Процедура ЗагрузитьПодписиФайла(ФайлСсылка, Подписи, ПараметрыСинхронизации)
	
	Если Не ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ЭлектроннаяПодпись") Тогда
		Возврат;
	КонецЕсли;

	МодульЭлектроннаяПодпись = ОбщегоНазначения.ОбщийМодуль("ЭлектроннаяПодпись");
	МодульЭлектроннаяПодписьКлиентСервер = ОбщегоНазначения.ОбщийМодуль("ЭлектроннаяПодписьКлиентСервер");
	ПодписиВФайле = МодульЭлектроннаяПодпись.УстановленныеПодписи(ФайлСсылка);
	
	ОшибкаНеУдалосьПрочитатьДанныеПодписи = "";
	
	ПодписиДляДобавления = Новый Массив;
	
	UID1C = Строка(ФайлСсылка.УникальныйИдентификатор());
	
	Для Каждого ПараметрыФайла Из Подписи Цикл 
		
		Если Не ПустаяСтрока(ПараметрыФайла.UID1C) И СтрНачинаетсяС(ПараметрыФайла.UID1C, UID1C) Тогда
			Продолжить; // Подпись уже синхронизировалась с облаком.
		КонецЕсли;
		
		ИмяФайла                 = ПараметрыФайла.ИмяФайла;
		АдресФайла               = ПараметрыФайла.Href;
		ИдентификаторEtag        = ПараметрыФайла.Etag;
		ДатаМодификацииФайла     = ПараметрыФайла.ДатаМодификацииФайла;
		ДлинаФайла               = ПараметрыФайла.ДлинаФайла;

		ТекстСобытия = НСтр("ru = 'Загрузка подписи с сервера: %1'");
		ЗаписатьВЖурналСобытийСинхронизацииФайлов(СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ТекстСобытия,
			ПараметрыФайла.ИмяФайла), ПараметрыСинхронизации.УчетнаяЗапись);

		РезультатЗагрузки = ВызватьМетодGET(АдресФайла, ИдентификаторEtag, ПараметрыСинхронизации,
			ДатаМодификацииФайла, ДлинаФайла);
			
		Если РезультатЗагрузки.Успешно И РезультатЗагрузки.АдресЗагруженногоФайла <> Неопределено Тогда
			
			Если Не РезультатЗагрузки.ЭтоПодпись Тогда
				ТекстСобытия = НСтр("ru = 'Файл не является подписью: %1'");
				ЗаписатьВЖурналСобытийСинхронизацииФайлов(СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					ТекстСобытия, ИмяФайла), ПараметрыСинхронизации.УчетнаяЗапись, УровеньЖурналаРегистрации.Ошибка);
				Продолжить;
			КонецЕсли;
			
			АдресЗагруженногоФайла = РезультатЗагрузки.АдресЗагруженногоФайла;
			
			Подпись = ПолучитьИзВременногоХранилища(АдресЗагруженногоФайла);
			
			ПодписьДобавлена = Ложь;
			Для Каждого ПодписьВФайле Из ПодписиВФайле Цикл
				Если ПодписьВФайле.Подпись = Подпись Тогда
					ПодписьДобавлена = Истина;
					Прервать;
				КонецЕсли;
			КонецЦикла;
			
			Если ПодписьДобавлена Тогда
				Продолжить;
			КонецЕсли;
			
			ДанныеПодписи = МодульЭлектроннаяПодписьКлиентСервер.НовыеСвойстваПодписи();
			ДанныеПодписи.Подпись = Подпись;
			
			РезультатЧтенияСвойствПодписи = МодульЭлектроннаяПодпись.СвойстваПодписи(Подпись);
			
			Если РезультатЧтенияСвойствПодписи.Успех <> Ложь Тогда
				ЗаполнитьЗначенияСвойств(ДанныеПодписи, РезультатЧтенияСвойствПодписи);
				ДанныеПодписи.Вставить("ДатаПодписиИзМетки", РезультатЧтенияСвойствПодписи.ДатаПодписиИзМетки);
				ДанныеПодписи.Вставить("НеподтвержденнаяДатаПодписи", РезультатЧтенияСвойствПодписи.НеподтвержденнаяДатаПодписи);
			Иначе
				Если ПустаяСтрока(ОшибкаНеУдалосьПрочитатьДанныеПодписи) Тогда
					ТекстСобытия = НСтр("ru = 'Не удалось прочитать данные подписей файла %1: %2'");
					ОшибкаНеУдалосьПрочитатьДанныеПодписи = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
						ТекстСобытия, ИмяФайла, РезультатЧтенияСвойствПодписи.ТекстОшибки);
				КонецЕсли;
			КонецЕсли;
			
			ПодписиДляДобавления.Добавить(ДанныеПодписи);

			ФайлUID1C = UID1C + ПостфиксДляПодписи(ПодписиВФайле.Количество() + ПодписиДляДобавления.ВГраница());
			ОбновитьUID1CФайла(АдресФайла, ФайлUID1C, ПараметрыСинхронизации);

			ТекстСообщения = НСтр("ru = 'Загружена подпись из облачного сервиса: ""%1""'");
			СтатусДляЖурналаРегистрации = УровеньЖурналаРегистрации.Информация;
		Иначе
			ТекстСообщения = НСтр("ru = 'Не удалось загрузить подпись ""%1"" из облачного сервиса по причине:'") + " "
				+ Символы.ПС + РезультатЗагрузки.ТекстОшибки;
			СтатусДляЖурналаРегистрации = УровеньЖурналаРегистрации.Ошибка;
		КонецЕсли;
		
		ЗаписатьВЖурналСобытийСинхронизацииФайлов(СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ТекстСообщения, ИмяФайла), 
			ПараметрыСинхронизации.УчетнаяЗапись, СтатусДляЖурналаРегистрации);
		
	КонецЦикла;
	
	Если ПодписиДляДобавления.Количество() > 0 Тогда
		МодульЭлектроннаяПодпись.ДобавитьПодпись(ФайлСсылка, ПодписиДляДобавления);
	КонецЕсли;
	
	Если ЗначениеЗаполнено(ОшибкаНеУдалосьПрочитатьДанныеПодписи) Тогда
		ЗаписатьВЖурналСобытийСинхронизацииФайлов(
			ОшибкаНеУдалосьПрочитатьДанныеПодписи, ПараметрыСинхронизации.УчетнаяЗапись, УровеньЖурналаРегистрации.Информация);
	КонецЕсли;

КонецПроцедуры

Функция НаименованиеЗашифрованногоФайла(Наименование)
	
	Возврат Лев(Наименование, СтрДлина(Наименование) - 4);
	
КонецФункции

Функция ЭтоПодписьИлиЗашифрованныеДанные(Href, ДвоичныеДанные = Неопределено)
	
	Результат = Новый Структура("Подпись, ЗашифрованныеДанные", Ложь, Ложь);
	
	Если Не ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ЭлектроннаяПодпись") Тогда
		Возврат Результат;
	КонецЕсли;
	
	Если СтрЗаканчиваетсяНа(НРег(Href), ".p7s") Тогда
		Результат.Подпись = Истина;
	ИначеЕсли СтрЗаканчиваетсяНа(НРег(Href), ".p7m") Тогда
		Результат.ЗашифрованныеДанные = Истина;
	КонецЕсли;
	
	Если Не Результат.ЗашифрованныеДанные И Не Результат.Подпись Или ДвоичныеДанные = Неопределено Тогда
		Возврат Результат;
	КонецЕсли;
	
	МодульЭлектроннаяПодписьСлужебныйКлиентСервер = ОбщегоНазначения.ОбщийМодуль("ЭлектроннаяПодписьСлужебныйКлиентСервер");
	ТипДанных = МодульЭлектроннаяПодписьСлужебныйКлиентСервер.ОпределитьТипДанных(ДвоичныеДанные);
	Результат.Подпись             = ТипДанных = "Подпись";
	Результат.ЗашифрованныеДанные = ТипДанных = "ЗашифрованныеДанные";
	
	Возврат Результат;
	
КонецФункции

Процедура ДобавитьПодписьИзСервиса(Подписи, СтрокаДереваФайлов)
	
	НачалоРасширенияПодписи = СтрДлина(СтрокаДереваФайлов.Href) - 3;
	
	Если Сред(СтрокаДереваФайлов.Href, НачалоРасширенияПодписи - 1, 1) = ")" Тогда
		Найдено = СтрНайти(СтрокаДереваФайлов.Href, "(", НаправлениеПоиска.СКонца);
		HrefФайла = Лев(СтрокаДереваФайлов.Href, Найдено - 1);
	Иначе
		HrefФайла = Лев(СтрокаДереваФайлов.Href, НачалоРасширенияПодписи - 1); 
	КонецЕсли;
	
	МассивПодписей = Подписи.Получить(HrefФайла);

	Если МассивПодписей = Неопределено Тогда
		МассивПодписей = Новый Массив;
	КонецЕсли;
	
	ПараметрыФайла = Новый Структура;
	ПараметрыФайла.Вставить("ИмяФайла",                 СтрокаДереваФайлов.ИмяФайла);
	ПараметрыФайла.Вставить("Href",                     СтрокаДереваФайлов.Href);
	ПараметрыФайла.Вставить("Etag",                     СтрокаДереваФайлов.Etag);
	ПараметрыФайла.Вставить("ДатаМодификацииФайла",     СтрокаДереваФайлов.ДатаМодификации);
	ПараметрыФайла.Вставить("ДлинаФайла",               СтрокаДереваФайлов.Длина);
	ПараметрыФайла.Вставить("UID1C",                    СтрокаДереваФайлов.UID1C);
	
	МассивПодписей.Добавить(ПараметрыФайла);
	
	Подписи.Вставить(HrefФайла, МассивПодписей);
	
КонецПроцедуры

Процедура ЗаполнитьДанныеИзОблачногоСервиса(СтрокиДереваФайлов, ТаблицаФайлов, ПараметрыСинхронизации, 
	ОбъектВладелец = Неопределено)
	
	Папки = ВладельцыФайловПоУникальнымИдентификаторам(СтрокиДереваФайлов);	
	Для каждого СтрокаДереваФайлов Из СтрокиДереваФайлов Цикл
		
		Если СтрокаДереваФайлов.ЭтоПапка = Истина Тогда 
			// Папка определяется сначала по UID1C, если не найдена, то по старому Href, т.к. UID может теряться при
			// редактировании, а новый Href еще не может быть найден в базе.
			// Если UID потерялся при редактировали и папку переместили в другую папку (Href поменялся), то она будет загружена в
			// новую карточку папки, и поиск по Href оправдан, т.к. он уникален на файловом сервере для каждой папки.
			ТекущаяПапкаФайлов = Неопределено;
			Если Не ПустаяСтрока(СтрокаДереваФайлов.UID1C) Тогда
				СтрокаТаблицы = Папки.Найти(Новый УникальныйИдентификатор(СтрокаДереваФайлов.UID1C), "УникальныйИдентификатор1С");
				Если СтрокаТаблицы <> Неопределено Тогда
					ТекущаяПапкаФайлов = СтрокаТаблицы.Файл; // ОпределяемыйТип.ВладелецФайлов
				КонецЕсли;
			КонецЕсли;
			
			Если ТекущаяПапкаФайлов = Неопределено Тогда
				// Может быть помечена на удаление, тогда у нее нет Href, и тут она не будет найдена.
				ТекущаяПапкаФайлов = НайтиСтрокуПоURI(СтрокаДереваФайлов.Href, ТаблицаФайлов, "Href");
			КонецЕсли; 
			
			СтараяСтрокаТаблицыФайлов = ТаблицаФайлов.Найти(ТекущаяПапкаФайлов.Ссылка, "ФайлСсылка");
			Если ТекущаяПапкаФайлов = Неопределено ИЛИ СтараяСтрокаТаблицыФайлов = Неопределено Тогда
				Продолжить;
			КонецЕсли;
			
			Если (ТекущаяПапкаФайлов <> Неопределено) ИЛИ (СтараяСтрокаТаблицыФайлов <> Неопределено) Тогда
				СтараяСтрокаТаблицыФайлов.ToHref = СтрокаДереваФайлов.Href;
				СтараяСтрокаТаблицыФайлов.ToEtag = СтрокаДереваФайлов.Etag;
				СтараяСтрокаТаблицыФайлов.РодительСервер = ОбъектВладелец;
				СтараяСтрокаТаблицыФайлов.НаименованиеСервер = СтрокаДереваФайлов.ИмяФайла;
				СтараяСтрокаТаблицыФайлов.ЕстьНаСервере = Истина;
				СтараяСтрокаТаблицыФайлов.ИзмененНаСервере = НЕ ЭтоОдинаковыеПутиURI(СтараяСтрокаТаблицыФайлов.ToHref,
					СтараяСтрокаТаблицыФайлов.Href);
			КонецЕсли; 
			
			// @skip-check query-in-loop - Рекурсивный алгоритм обработки дерева.
			ЗаполнитьДанныеИзОблачногоСервиса(СтрокаДереваФайлов.Строки, ТаблицаФайлов, ПараметрыСинхронизации, 
				ТекущаяПапкаФайлов.Ссылка);
			Продолжить;
			
		КонецЕсли; 
		
		// Это файл.
		// Файл определяется сначала по UID1C, если не найден, то по старому Href, т.к. UID может теряться при
		// редактировании, а новый Href еще не может быть найден в базе.
		// Если UID потерялся при редактировали и файл переместили в другую папку (Href поменялся), то он будет загружен в
		// новую карточку файла, и поиск по Href оправдан, т.к. он уникален на файловом сервере для каждого файла.
		
		ТекущийФайл = НайтиСтрокуПоURI(СтрокаДереваФайлов.Href, ТаблицаФайлов, "Href");
		Если (ТекущийФайл <> Неопределено) И (ТаблицаФайлов.Найти(ТекущийФайл.ФайлСсылка, "ФайлСсылка") <> Неопределено) Тогда
			// Обновим ToHref
			СтараяСтрокаТаблицыФайлов = ТаблицаФайлов.Найти(ТекущийФайл.ФайлСсылка, "ФайлСсылка");
			СтараяСтрокаТаблицыФайлов.ToHref = СтрокаДереваФайлов.Href;
			СтараяСтрокаТаблицыФайлов.ToEtag = СтрокаДереваФайлов.Etag;
			СтараяСтрокаТаблицыФайлов.РодительСервер = ОбъектВладелец;
			СтараяСтрокаТаблицыФайлов.НаименованиеСервер = СтрокаДереваФайлов.ИмяФайла;
			СтараяСтрокаТаблицыФайлов.ЕстьНаСервере = Истина;
			СтараяСтрокаТаблицыФайлов.ИзмененНаСервере = НЕ ЭтоОдинаковыеПутиURI(СтараяСтрокаТаблицыФайлов.ToHref,
				СтараяСтрокаТаблицыФайлов.Href);
		КонецЕсли; 
			
	КонецЦикла; 
	
КонецПроцедуры

Функция ВладельцыФайловПоУникальнымИдентификаторам(СтрокиДереваФайлов)
	УникальныеИдентификаторы = Новый Массив; 
	Для каждого СтрокаДереваФайлов Из СтрокиДереваФайлов Цикл
		Если СтрокаДереваФайлов.ЭтоПапка = Истина И Не ПустаяСтрока(СтрокаДереваФайлов.UID1C) Тогда 
			УникальныеИдентификаторы.Добавить(Новый УникальныйИдентификатор(СтрокаДереваФайлов.UID1C));
		КонецЕсли; 
	КонецЦикла;
	
	Если УникальныеИдентификаторы.Количество() = 0 Тогда
		Возврат Новый ТаблицаЗначений;
	КонецЕсли;
		
	Запрос = Новый Запрос(
		"ВЫБРАТЬ
		|	СтатусыСинхронизацииФайловСОблачнымСервисом.Файл КАК Файл,
		|	СтатусыСинхронизацииФайловСОблачнымСервисом.УникальныйИдентификатор1С КАК УникальныйИдентификатор1С
		|ИЗ
		|	РегистрСведений.СтатусыСинхронизацииФайловСОблачнымСервисом КАК СтатусыСинхронизацииФайловСОблачнымСервисом
		|ГДЕ
		|	СтатусыСинхронизацииФайловСОблачнымСервисом.УникальныйИдентификатор1С В (&УникальныеИдентификаторы)");
	Запрос.УстановитьПараметр("УникальныеИдентификаторы", УникальныеИдентификаторы);
	Возврат Запрос.Выполнить().Выгрузить();

КонецФункции

Функция ВладелецФайлаПоУникальномуИдентификатору(UID1C)

	Запрос = Новый Запрос(
		"ВЫБРАТЬ
		|	СтатусыСинхронизацииФайловСОблачнымСервисом.Файл КАК Файл
		|ИЗ
		|	РегистрСведений.СтатусыСинхронизацииФайловСОблачнымСервисом КАК СтатусыСинхронизацииФайловСОблачнымСервисом
		|ГДЕ
		|	СтатусыСинхронизацииФайловСОблачнымСервисом.УникальныйИдентификатор1С = &УникальныйИдентификатор");
	Если ТипЗнч(UID1C) = Тип("Строка") Тогда
		Запрос.УстановитьПараметр("УникальныйИдентификатор", Новый УникальныйИдентификатор(UID1C));
	Иначе	
		Запрос.УстановитьПараметр("УникальныйИдентификатор", UID1C);
	КонецЕсли;
	Результат = Запрос.Выполнить().Выгрузить();
	Возврат ?(Результат.Количество() > 0, Результат[0].Файл, Неопределено);

КонецФункции

// Параметры:
//  УчетнаяЗапись - СправочникСсылка.УчетныеЗаписиСинхронизацииФайлов
// 
// Возвращаемое значение:
//  Структура:
//   * УчетнаяЗапись - СправочникСсылка.УчетныеЗаписиСинхронизацииФайлов
//   * АдресСервера - Строка
//   * КорневаяПапка - Строка
//   * АвторФайлов - СправочникСсылка.УчетныеЗаписиСинхронизацииФайлов, СправочникСсылка.Пользователи
//   * СтруктураАдресаСервера - Строка
//   * Ответ - Строка
//   * Логин - Строка
//   * Пароль - Строка
//
Функция СвойстваСинхронизацииФайлов(УчетнаяЗапись)

	СтруктураВозврата = Новый Структура("СтруктураАдресаСервера, Ответ, Логин, Пароль");
	Запрос = Новый Запрос(
		"ВЫБРАТЬ
		|	УчетныеЗаписиСинхронизацииФайлов.Ссылка КАК УчетнаяЗапись,
		|	УчетныеЗаписиСинхронизацииФайлов.Сервис КАК АдресСервера,
		|	УчетныеЗаписиСинхронизацииФайлов.КорневаяПапка КАК КорневаяПапка,
		|	УчетныеЗаписиСинхронизацииФайлов.АвторФайлов КАК АвторФайлов
		|ИЗ
		|	Справочник.УчетныеЗаписиСинхронизацииФайлов КАК УчетныеЗаписиСинхронизацииФайлов
		|ГДЕ
		|	УчетныеЗаписиСинхронизацииФайлов.Ссылка = &Ссылка
		|	И УчетныеЗаписиСинхронизацииФайлов.ПометкаУдаления = ЛОЖЬ");
	
	Запрос.УстановитьПараметр("Ссылка", УчетнаяЗапись);
	Результат = Запрос.Выполнить().Выгрузить();
	Если Результат.Количество() = 0 Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	Для каждого КолонкаРезультата Из Результат.Колонки Цикл
		СтруктураВозврата.Вставить(КолонкаРезультата.Имя, Результат[0][КолонкаРезультата.Имя]);
	КонецЦикла; 
	
	Если Не ПустаяСтрока(СтруктураВозврата.КорневаяПапка) Тогда
		СтруктураВозврата.АдресСервера = СтруктураВозврата.АдресСервера + "/" + СтруктураВозврата.КорневаяПапка;
	КонецЕсли;
	
	Если ПустаяСтрока(СтруктураВозврата.АвторФайлов) Тогда
		СтруктураВозврата.АвторФайлов = УчетнаяЗапись;
	КонецЕсли;
	
	СтруктураВозврата.СтруктураАдресаСервера = СтруктураURIРаскодированная(СтруктураВозврата.АдресСервера);
	
	УстановитьПривилегированныйРежим(Истина);
	СтруктураВозврата.Логин =  ОбщегоНазначения.ПрочитатьДанныеИзБезопасногоХранилища(УчетнаяЗапись, "Логин");
	СтруктураВозврата.Пароль = ОбщегоНазначения.ПрочитатьДанныеИзБезопасногоХранилища(УчетнаяЗапись);
	УстановитьПривилегированныйРежим(Ложь);
	
	Возврат СтруктураВозврата;

КонецФункции

Процедура СинхронизироватьФайлыСОблачнымСервисом(УчетнаяЗапись)
	
	ПараметрыСинхронизации = СвойстваСинхронизацииФайлов(УчетнаяЗапись);
	Если ПараметрыСинхронизации = Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	ТекстСобытия = НСтр("ru = 'Начало синхронизации файлов с облачным сервисом.'");
	ЗаписатьВЖурналСобытийСинхронизацииФайлов(ТекстСобытия, ПараметрыСинхронизации.УчетнаяЗапись);
	
	ВыполнитьСинхронизациюФайловОблачнымСервисом(ПараметрыСинхронизации);
	
	ТекстСобытия = НСтр("ru = 'Завершена синхронизация файлов с облачным сервисом.'");
	ЗаписатьВЖурналСобытийСинхронизацииФайлов(ТекстСобытия, ПараметрыСинхронизации.УчетнаяЗапись);

КонецПроцедуры

Процедура ВыполнитьСинхронизациюФайловОблачнымСервисом(ПараметрыСинхронизации)
	
	// Корневая запись о начале синхронизации
	ЗапомнитьСерверныеДанныеСсылки("", "", "", Ложь, Неопределено, Ложь, ПараметрыСинхронизации.УчетнаяЗапись);

	ДеревоФайловСервера = СформироватьСтруктуруДереваФайловСервера();
	АдресСервера        = КодироватьURIПоСтруктуре(ПараметрыСинхронизации.СтруктураАдресаСервера);
	
	Если Не ПустаяСтрока(ПараметрыСинхронизации.КорневаяПапка) Тогда
		
		ЗаголовкиHTTP                 = Новый Соответствие;
		ЗаголовкиHTTP["User-Agent"]   = "1C Enterprise 8.3";
		ЗаголовкиHTTP["Content-type"] = "text/xml";
		ЗаголовкиHTTP["Accept"]       = "text/xml";
		ЗаголовкиHTTP["Depth"]        = "0";
		
		Попытка
			Результат = ВыполнитьМетодWebdav("PROPFIND", АдресСервера, ЗаголовкиHTTP, ПараметрыСинхронизации);
			Если Не Результат.Успешно Тогда
				ВызватьМетодMKCOL(АдресСервера, ПараметрыСинхронизации);
			КонецЕсли;
		Исключение
			ТекстСобытия = НСтр("ru = 'Не удалось создать корневую папку на сервере, синхронизация файлов не выполнена.'");
			ЗаписатьВЖурналСобытийСинхронизацииФайлов(ТекстСобытия, ПараметрыСинхронизации.УчетнаяЗапись, УровеньЖурналаРегистрации.Ошибка);
			Возврат;
		КонецПопытки
		
	КонецЕсли;
	
	Отказ = Ложь;
	СинхронизацияВыполнена = Истина;
	
	СкачатьДеревоФайловРекурсивно(ДеревоФайловСервера.Строки, АдресСервера, ПараметрыСинхронизации, Отказ);
	Если Отказ = Истина Тогда
		ТекстСобытия = НСтр("ru = 'Не удалось загрузить структуру файлов с сервера, синхронизация файлов не выполнена.'");
		ЗаписатьВЖурналСобытийСинхронизацииФайлов(ТекстСобытия, ПараметрыСинхронизации.УчетнаяЗапись, УровеньЖурналаРегистрации.Ошибка);
		Возврат;
	КонецЕсли;
	
	// Сравниваем с деревом файлов в системе, синхронизация идет по уникальному идентификатору.
	ТаблицаФайлов = ВыбратьДанныеПоПравилам(ПараметрыСинхронизации.УчетнаяЗапись);
	Если ТаблицаФайлов = Неопределено Тогда
		ТекстСобытия = НСтр("ru = 'Не удалось получить таблицу файлов из информационной базы, синхронизация файлов не выполнена.'");
		ЗаписатьВЖурналСобытийСинхронизацииФайлов(ТекстСобытия, ПараметрыСинхронизации.УчетнаяЗапись, УровеньЖурналаРегистрации.Ошибка);
		Возврат;
	КонецЕсли;
	
	Для каждого СтрокаТаблицы Из ТаблицаФайлов Цикл
		СтрокаТаблицы.UID1C = Строка(СтрокаТаблицы.ФайлСсылка.УникальныйИдентификатор());
	КонецЦикла;
	
	Подписи = Новый Соответствие;
	
	// Перебираем дерево, загружаем и добавляем те, которых нет в базе, в таблицу, а по старым заполняем реквизиты с сервера.
	ЗагрузитьНовыеПрисоединенныеФайлы(ДеревоФайловСервера.Строки, ТаблицаФайлов, ПараметрыСинхронизации, Подписи);
	
	ВычислитьУровеньРекурсивно(ТаблицаФайлов);
	ТаблицаФайлов.Индексы.Добавить("ФайлСсылка");
	
	ТаблицаФайлов.Сортировать("Уровень, РодительСортировка, ЭтоПапка УБЫВ");
	Если Не СинхронизироватьФайлы(ТаблицаФайлов, ПараметрыСинхронизации, АдресСервера, Подписи, Ложь) Тогда
		СинхронизацияВыполнена = Ложь;
	КонецЕсли;
	
	ТаблицаФайлов.Сортировать("Уровень УБЫВ, РодительСортировка, ЭтоПапка УБЫВ");
	Если Не СинхронизироватьФайлы(ТаблицаФайлов, ПараметрыСинхронизации, АдресСервера, Неопределено, Истина) Тогда
		СинхронизацияВыполнена = Ложь;
	КонецЕсли;
	
	ЗаписатьРезультатСинхронизации(ПараметрыСинхронизации.УчетнаяЗапись, СинхронизацияВыполнена);
	
КонецПроцедуры

Функция СинхронизироватьФайлы(ТаблицаФайлов, ПараметрыСинхронизации, АдресСервера, Подписи, ЭтоУдалениеФайлов = Ложь)
	
	СинхронизацияВыполнена = Истина;
	Для Каждого СтрокаТаблицы Из ТаблицаФайлов Цикл
		
		Если СтрокаТаблицы.Обработан Тогда
			УстановитьСтатусСинхронизацииФайла(СтрокаТаблицы, ПараметрыСинхронизации.УчетнаяЗапись);
			Продолжить;
		КонецЕсли;
		
		ОбновитьСтатусСинхронизацииФайла = Ложь;
		
		СозданНовыйВБазе            = (НЕ ЗначениеЗаполнено(СтрокаТаблицы.Href)) И (НЕ ЗначениеЗаполнено(СтрокаТаблицы.ToHref));
		
		ИзмененВБазе                = ЗначениеЗаполнено(СтрокаТаблицы.Изменения); // что-то поменялось
		ИзмененоСодержимоеНаСервере = ЗначениеЗаполнено(СтрокаТаблицы.ToEtag) И (СтрокаТаблицы.Etag <> СтрокаТаблицы.ToEtag); // поменялось содержимое
		ИзмененНаСервере            = ИзмененоСодержимоеНаСервере ИЛИ СтрокаТаблицы.ИзмененНаСервере; // поменялось имя/подчиненность или содержание
		
		УдаленВБазе                 = СтрокаТаблицы.ПометкаУдаления;
		УдаленНаСервере             = ЗначениеЗаполнено(СтрокаТаблицы.Href) И НЕ ЗначениеЗаполнено(СтрокаТаблицы.ToHref);
		
		НачатьТранзакцию();
		Попытка
			Если ЭтоСсылкаНаФайл(СтрокаТаблицы.ФайлСсылка) Тогда
				БлокировкаДанных = Новый БлокировкаДанных;
				ЭлементБлокировкиДанных = БлокировкаДанных.Добавить(Метаданные.НайтиПоТипу(ТипЗнч(СтрокаТаблицы.ФайлСсылка)).ПолноеИмя());
				ЭлементБлокировкиДанных.УстановитьЗначение("Ссылка", СтрокаТаблицы.ФайлСсылка);
				ЗаблокироватьСтатусСинхронизацииФайла(БлокировкаДанных, СтрокаТаблицы.ФайлСсылка, ПараметрыСинхронизации.УчетнаяЗапись);
				БлокировкаДанных.Заблокировать();  
			КонецЕсли;
			
			Если ЭтоУдалениеФайлов Тогда
				Если УдаленНаСервере И НЕ УдаленВБазе Тогда
					ОбновитьСтатусСинхронизацииФайла = УдалитьФайлВОблачномСервисе(ПараметрыСинхронизации, СтрокаТаблицы);
				КонецЕсли;
			Иначе
				
				Если СозданНовыйВБазе И НЕ УдаленВБазе Тогда
					// Загрузить файл на облачный сервер
					ОбновитьСтатусСинхронизацииФайла = СоздатьФайлВОблачномСервисе(АдресСервера, ПараметрыСинхронизации, СтрокаТаблицы, ТаблицаФайлов);
					
				ИначеЕсли (ИзмененВБазе ИЛИ ИзмененНаСервере) И НЕ (УдаленВБазе ИЛИ УдаленНаСервере) Тогда
					
					Если ИзмененНаСервере И СтрокаТаблицы.ПодписанЭП Тогда
						ВызватьИсключение НСтр("ru = 'Не предусмотрена загрузка из облака измененных файлов, имеющих подписи в программе.
							|Удалите подписи в программе и добавьте их в облако, чтобы синхронизировать файл.'")
					КонецЕсли;
					
					Если ИзмененНаСервере И НЕ ИзмененВБазе Тогда
						ОбновитьСтатусСинхронизацииФайла = ИзменитьФайлВОблачномСервисе(ИзмененоСодержимоеНаСервере, ОбновитьСтатусСинхронизацииФайла, ПараметрыСинхронизации, СтрокаТаблицы);
					КонецЕсли;
					
				КонецЕсли;
				
			КонецЕсли;
			
			Если ОбновитьСтатусСинхронизацииФайла Тогда
				// Записываем обновления в регистр сведений статусов.
				Если СтрокаТаблицы.ПометкаУдаления Тогда
					// Удаляем последний Href, чтобы повторно не идентифицировать.
					УдалитьСтатусСинхронизацииФайла(СтрокаТаблицы.ФайлСсылка, ПараметрыСинхронизации.УчетнаяЗапись);
				Иначе
					УстановитьСтатусСинхронизацииФайла(СтрокаТаблицы, ПараметрыСинхронизации.УчетнаяЗапись);
				КонецЕсли;
			КонецЕсли;
			
			ЗафиксироватьТранзакцию();
		Исключение
			ОтменитьТранзакцию();
			СтрокаТаблицы.ДатаСинхронизации = ТекущаяДатаСеанса();
			УстановитьСтатусСинхронизацииФайла(СтрокаТаблицы, ПараметрыСинхронизации.УчетнаяЗапись);
			
			СинхронизацияВыполнена = Ложь;
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Не удалось синхронизировать файл ""%1"" по причине:'"), Строка(СтрокаТаблицы.ФайлСсылка))
				+ Символы.ПС + ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
			ЗаписатьВЖурналСобытийСинхронизацииФайлов(ТекстОшибки, ПараметрыСинхронизации.УчетнаяЗапись, УровеньЖурналаРегистрации.Ошибка);
		КонецПопытки;
		
	КонецЦикла;
	
	Если ЗначениеЗаполнено(Подписи) Тогда
		
		Для Каждого КлючИЗначение Из Подписи Цикл
			
			ТекущийФайл = НайтиСтрокуПоURI(КлючИЗначение.Ключ, ТаблицаФайлов, "ToHref", Истина);
			
			Если ТекущийФайл = Неопределено Тогда
				ТекстСобытия = НСтр("ru = 'Не найден файл %1 для загрузки подписей.'");
				ЗаписатьВЖурналСобытийСинхронизацииФайлов(СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					ТекстСобытия, КлючИЗначение.Ключ),
				ПараметрыСинхронизации.УчетнаяЗапись, УровеньЖурналаРегистрации.Ошибка);
				Продолжить;
			КонецЕсли;
			
			Если ТекущийФайл.Обработан Или Не ТекущийФайл.ИзмененНаСервере Тогда
				ЗагрузитьПодписиФайла(ТекущийФайл.ФайлСсылка, КлючИЗначение.Значение, ПараметрыСинхронизации);
			КонецЕсли;
		
		КонецЦикла;
		
	КонецЕсли;

	Возврат СинхронизацияВыполнена;
	
КонецФункции

Процедура ЗаписатьРезультатСинхронизации(УчетнаяЗапись, Знач СинхронизацияВыполнена)
	
	БлокировкаДанных = Новый БлокировкаДанных;
	ЭлементБлокировкиДанных = БлокировкаДанных.Добавить("РегистрСведений.СтатусыСинхронизацииФайловСОблачнымСервисом");
	ЭлементБлокировкиДанных.УстановитьЗначение("Файл", "");
	ЭлементБлокировкиДанных = БлокировкаДанных.Добавить("РегистрСведений.СтатусыСинхронизацииФайловСОблачнымСервисом");
	ЭлементБлокировкиДанных.УстановитьЗначение("УчетнаяЗапись", УчетнаяЗапись);
	
	НачатьТранзакцию();
	Попытка
		
		БлокировкаДанных.Заблокировать();
		
		НаборЗаписей = РегистрыСведений.СтатусыСинхронизацииФайловСОблачнымСервисом.СоздатьНаборЗаписей();
		НаборЗаписей.Отбор.Файл.Установить("", Истина);
		НаборЗаписей.Отбор.УчетнаяЗапись.Установить(УчетнаяЗапись, Истина);
		НаборЗаписей.Прочитать();
		
		Если НаборЗаписей.Количество() > 0 Тогда
			Запись                             = НаборЗаписей.Получить(0);
			Запись.ДатаСинхронизацииЗавершение = ТекущаяДатаСеанса();
			Запись.Синхронизирован             = СинхронизацияВыполнена;
			НаборЗаписей.Записать();
		КонецЕсли;
		
		ЗафиксироватьТранзакцию();
		
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

Функция СформироватьСтруктуруДереваФайловСервера()
	
	ДеревоФайловСервера = Новый ДеревоЗначений;
	ДеревоФайловСервера.Колонки.Добавить("Href");
	ДеревоФайловСервера.Колонки.Добавить("UID1C");
	ДеревоФайловСервера.Колонки.Добавить("UID1CНеПоддерживается");
	ДеревоФайловСервера.Колонки.Добавить("Etag");
	ДеревоФайловСервера.Колонки.Добавить("ИмяФайла");
	ДеревоФайловСервера.Колонки.Добавить("ЭтоПапка");
	ДеревоФайловСервера.Колонки.Добавить("ДатаМодификации");
	ДеревоФайловСервера.Колонки.Добавить("Длина");
	Возврат ДеревоФайловСервера;
	
КонецФункции

Функция ИзменитьФайлВОблачномСервисе(Знач ИзмененоСодержимоеНаСервере, ОбновитьСтатусСинхронизацииФайла, Знач ПараметрыСинхронизации, Знач СтрокаТаблицы)
	
	Блокировка = Новый БлокировкаДанных;
	ЭлементБлокировки = Блокировка.Добавить(СтрокаТаблицы.ФайлСсылка.Метаданные().ПолноеИмя());
	ЭлементБлокировки.УстановитьЗначение("Ссылка", СтрокаТаблицы.ФайлСсылка);
	
	НачатьТранзакцию();
	Попытка
		
		Блокировка.Заблокировать();
		
		// загрузим с сервера
		Если СтрокаТаблицы.ЭтоПапка Тогда
			// Возможно можно отследить переименование.
			ОбъектСтрокиТаблицы                 = СтрокаТаблицы.ФайлСсылка.ПолучитьОбъект();
			ОбъектСтрокиТаблицы.Наименование    = СтрокаТаблицы.НаименованиеСервер;
			ОбъектСтрокиТаблицы.Родитель        = Неопределено;
			ОбъектСтрокиТаблицы.ПометкаУдаления = Ложь;
			ОбъектСтрокиТаблицы.Записать();
			
			СтрокаТаблицы.Наименование    = СтрокаТаблицы.НаименованиеСервер;
			СтрокаТаблицы.Изменения       = СтрокаТаблицы.ФайлСсылка;
			СтрокаТаблицы.Родитель        = СтрокаТаблицы.РодительСервер;
			СтрокаТаблицы.ПометкаУдаления = Ложь;
			
		Иначе
			
			СтруктураИмениФайла = Новый Файл(СтрокаТаблицы.НаименованиеСервер);
			НовоеРасширениеФайла = ОбщегоНазначенияКлиентСервер.РасширениеБезТочки(СтруктураИмениФайла.Расширение);
			// Загружаем только если поменялось содержимое, т.е. Etag, иначе обновляем реквизиты.
			Если ИзмененоСодержимоеНаСервере ИЛИ (НовоеРасширениеФайла <> СтрокаТаблицы.Расширение)
				ИЛИ (НовоеРасширениеФайла <> СтрокаТаблицы.Расширение)
				ИЛИ СтрокаТаблицы.Зашифрован <> СтрокаТаблицы.ЗашифрованНаСервере Тогда
				
				ПараметрыФайла = Новый Структура;
				ПараметрыФайла.Вставить("ИмяФайла",                 СтрокаТаблицы.НаименованиеСервер);
				ПараметрыФайла.Вставить("Href",                     СтрокаТаблицы.ToHref);
				ПараметрыФайла.Вставить("Etag",                     СтрокаТаблицы.ToEtag);
				ПараметрыФайла.Вставить("ДатаМодификацииФайла",     Неопределено);
				ПараметрыФайла.Вставить("ДлинаФайла",               Неопределено);
				ПараметрыФайла.Вставить("ДляПользователя",          ПараметрыСинхронизации.АвторФайлов);
				ПараметрыФайла.Вставить("ОбъектВладелец",           СтрокаТаблицы.Родитель);
				ПараметрыФайла.Вставить("СсылкаСуществующегоФайла", СтрокаТаблицы.ФайлСсылка);
				ПараметрыФайла.Вставить("ПараметрыСинхронизации",   ПараметрыСинхронизации);
				ПараметрыФайла.Вставить("Зашифрован",               СтрокаТаблицы.Зашифрован);
				
				ЗагрузитьФайлССервера(ПараметрыФайла, СтрокаТаблицы.ЭтоФайл);
				
			КонецЕсли;
			
			ОбъектСтрокиТаблицы = СтрокаТаблицы.ФайлСсылка.ПолучитьОбъект();
			ОбъектСтрокиТаблицы.Наименование    = СтруктураИмениФайла.ИмяБезРасширения;
			ОбъектСтрокиТаблицы.ПометкаУдаления = Ложь;
			ОбъектСтрокиТаблицы.Записать();
			
			СтрокаТаблицы.Наименование    = СтрокаТаблицы.НаименованиеСервер;
			СтрокаТаблицы.Изменения       = СтрокаТаблицы.ФайлСсылка;
			СтрокаТаблицы.Родитель        = СтрокаТаблицы.РодительСервер;
			СтрокаТаблицы.ПометкаУдаления = Ложь;
			
		КонецЕсли;
		
		ЗафиксироватьТранзакцию();
		
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
	СтрокаТаблицы.Обработан = Истина;
	СтрокаТаблицы.ДатаСинхронизации = ТекущаяДатаСеанса();
	
	ТекстСобытия = НСтр("ru = 'Файл изменен: %1'");
	ЗаписатьВЖурналСобытийСинхронизацииФайлов(СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			ТекстСобытия, СтрокаТаблицы.Наименование), 
		ПараметрыСинхронизации.УчетнаяЗапись);
	
	Возврат Истина;
	
КонецФункции

Функция УдалитьФайлВОблачномСервисе(Знач ПараметрыСинхронизации, Знач СтрокаТаблицы)
	
	Если Не ЗначениеЗаполнено(СтрокаТаблицы.ФайлСсылка) Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Если Не ЭтоСсылкаНаФайл(СтрокаТаблицы.ФайлСсылка) Тогда
		СтрокаТаблицы.Обработан = Истина;
		Возврат Ложь;
	КонецЕсли;
	
	НачатьТранзакцию();
	Попытка
		
		ОсвободитьФайл(СтрокаТаблицы.ФайлСсылка);
		СтрокаТаблицы.ФайлСсылка.ПолучитьОбъект().УстановитьПометкуУдаления(Истина, Ложь);
		ЗафиксироватьТранзакцию();
		
	Исключение
		
		ОтменитьТранзакцию();
		ЗаписатьВЖурналСобытийСинхронизацииФайлов(
			СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = 'Не удален файл: %1'"), СтрокаТаблицы.Наименование), 
			ПараметрыСинхронизации.УчетнаяЗапись, УровеньЖурналаРегистрации.Ошибка);
		Возврат Ложь;
		
	КонецПопытки;
	
	СтрокаТаблицы.ПометкаУдаления = Истина;
	СтрокаТаблицы.Изменения       = СтрокаТаблицы.ФайлСсылка;
	СтрокаТаблицы.Обработан       = Истина;
	СтрокаТаблицы.ДатаСинхронизации  = ТекущаяДатаСеанса();
	
	ЗаписатьВЖурналСобытийСинхронизацииФайлов(
		СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = 'Удален файл: %1'"), СтрокаТаблицы.Наименование), 
		ПараметрыСинхронизации.УчетнаяЗапись);
	
	Возврат Истина;

КонецФункции

Функция ЭтоСсылкаНаФайл(ОбъектВладелец)
	
	МассивТиповФайлов = Метаданные.ОпределяемыеТипы.ПрисоединенныйФайл.Тип.Типы();
	Возврат МассивТиповФайлов.Найти(ТипЗнч(ОбъектВладелец)) <> Неопределено;
	
КонецФункции

Функция РассчитатьТаймаут(Размер)
	
	Таймаут = Цел(Размер / 8192); // размер в мегабайтах * 128
	Если Таймаут < 10 Тогда
		Возврат 10;
	ИначеЕсли Таймаут > 43200 Тогда
		Возврат 43200;
	КонецЕсли;
	
	Возврат Таймаут;
	
КонецФункции

Функция СоздатьФайлВОблачномСервисе(Знач АдресСервера, Знач ПараметрыСинхронизации, Знач СтрокаТаблицы, Знач ТаблицаФайлов)
	
	// отправим новый на сервер
	СтрокаТаблицы.Наименование = ОбщегоНазначенияКлиентСервер.ЗаменитьНедопустимыеСимволыВИмениФайла(СтрокаТаблицы.Наименование, "-");
	СтрокаТаблицы.ToHref       = ЗакончитьБезСлеша(АдресСервера) + НачатьСлешем(ЗакончитьБезСлеша(РассчитатьHref(СтрокаТаблицы,ТаблицаФайлов)));
	
	Если ОбщегоНазначения.ОбъектЯвляетсяГруппой(СтрокаТаблицы.ФайлСсылка) Тогда
		ВызватьМетодMKCOL(СтрокаТаблицы.ToHref, ПараметрыСинхронизации);
	ИначеЕсли СтрокаТаблицы.ЭтоПапка Тогда
		ВызватьМетодMKCOL(СтрокаТаблицы.ToHref, ПараметрыСинхронизации);
	Иначе
		
		Если СтрокаТаблицы.Зашифрован Тогда
			СтрокаТаблицы.ToHref = СтрокаТаблицы.ToHref + ".p7m";
		КонецЕсли;
		
		СтрокаТаблицы.ToEtag = ВызватьМетодPUT(СтрокаТаблицы.ToHref, СтрокаТаблицы.ФайлСсылка, ПараметрыСинхронизации, СтрокаТаблицы.ЭтоФайл);
		
		Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ЭлектроннаяПодпись") И СтрокаТаблицы.ПодписанЭП Тогда
			
			МодульЭлектроннаяПодпись = ОбщегоНазначения.ОбщийМодуль("ЭлектроннаяПодпись");
			ЭлектронныеПодписи = МодульЭлектроннаяПодпись.УстановленныеПодписи(СтрокаТаблицы.ФайлСсылка);
			НомерПодписи = 0;
			
			Для Каждого Подпись Из ЭлектронныеПодписи Цикл
				Постфикс = ПостфиксДляПодписи(НомерПодписи);

				Если СтрокаТаблицы.Зашифрован Тогда
					ToHrefПодписи = НаименованиеЗашифрованногоФайла(СтрокаТаблицы.ToHref) + Постфикс;
				Иначе
					ToHrefПодписи = СтрокаТаблицы.ToHref + Постфикс;
				КонецЕсли;

				UID1CПодписи = СтрокаТаблицы.UID1C + Постфикс;
				ВызватьМетодPUT(ToHrefПодписи, Подпись.Подпись, ПараметрыСинхронизации, Истина);
				ОбновитьUID1CФайла(ToHrefПодписи, UID1CПодписи, ПараметрыСинхронизации);
				НомерПодписи = НомерПодписи + 1;
			КонецЦикла;
			
		КонецЕсли;
	КонецЕсли;
	
	ОбновитьUID1CФайла(СтрокаТаблицы.ToHref, СтрокаТаблицы.UID1C, ПараметрыСинхронизации);
	
	СтрокаТаблицы.РодительСервер     = СтрокаТаблицы.Родитель;
	СтрокаТаблицы.НаименованиеСервер = СтрокаТаблицы.Наименование;
	СтрокаТаблицы.ЕстьНаСервере      = Истина;
	СтрокаТаблицы.Обработан          = Истина;
	СтрокаТаблицы.ДатаСинхронизации  = ТекущаяДатаСеанса();
	
	ОбъектЯвляетсяГруппой = ОбщегоНазначения.ОбъектЯвляетсяГруппой(СтрокаТаблицы.ФайлСсылка);
	Если Не СтрокаТаблицы.ЭтоФайл
		И Не СтрокаТаблицы.ЭтоПапка
		И Не ОбъектЯвляетсяГруппой Тогда
		Если ОбщегоНазначения.ЗначениеРеквизитаОбъекта(СтрокаТаблицы.ФайлСсылка, "Редактирует") <> ПараметрыСинхронизации.АвторФайлов Тогда
			ЗанятьФайлДляРедактированияСервер(СтрокаТаблицы.ФайлСсылка, ПараметрыСинхронизации.АвторФайлов);
		КонецЕсли;
	ИначеЕсли Не СтрокаТаблицы.ЭтоПапка И Не ОбъектЯвляетсяГруппой Тогда
		ДанныеФайла = РаботаСФайламиСлужебныйВызовСервера.ДанныеФайла(СтрокаТаблицы.ФайлСсылка);
		
		Если СтрокаТаблицы.ПодписанЭП Тогда
			ДополнительныеСвойства = Новый Структура("ЗаписьПодписанногоОбъекта", Истина);
		Иначе
			ДополнительныеСвойства = Неопределено;
		КонецЕсли;
		
		ПараметрыЗанятияФайла = РаботаСФайламиСлужебныйКлиентСервер.ПараметрыЗанятияФайла();
		ПараметрыЗанятияФайла.Пользователь = ПараметрыСинхронизации.АвторФайлов;
		ПараметрыЗанятияФайла.ДополнительныеСвойства = ДополнительныеСвойства;
				
		РаботаСФайламиСлужебныйВызовСервера.ЗанятьФайл(ДанныеФайла, , ПараметрыЗанятияФайла);
	КонецЕсли;
	
	ТекстСобытия = НСтр("ru = 'Создан объект в облачном сервисе %1'");
	ЗаписатьВЖурналСобытийСинхронизацииФайлов(
		СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ТекстСобытия, СтрокаТаблицы.Наименование), 
		ПараметрыСинхронизации.УчетнаяЗапись);
	
	Возврат Истина;
	
КонецФункции

Функция ПостфиксДляПодписи(НомерПодписи)
	
	Возврат ?(НомерПодписи = 0, "", "(" + НомерПодписи + ")") + ".p7s"
	
КонецФункции

Функция ВыбратьДанныеПоПравилам(УчетнаяЗапись, Синхронизировать = ИСТИНА)
	
	ЗапросНастройкиСинхронизации = Новый Запрос;
	ЗапросНастройкиСинхронизации.Текст = 
	"ВЫБРАТЬ
	|	НастройкиСинхронизацииФайлов.ВладелецФайла КАК ВладелецФайла,
	|	НастройкиСинхронизацииФайлов.ТипВладельцаФайла КАК ТипВладельцаФайла,
	|	ИдентификаторыОбъектовМетаданных.Ссылка КАК ИдентификаторВладельца,
	|	ВЫБОР
	|		КОГДА ТИПЗНАЧЕНИЯ(ИдентификаторыОбъектовМетаданных.Ссылка) <> ТИПЗНАЧЕНИЯ(НастройкиСинхронизацииФайлов.ВладелецФайла)
	|			ТОГДА ИСТИНА
	|		ИНАЧЕ ЛОЖЬ
	|	КОНЕЦ КАК ЭтоНастройкаДляЭлементаСправочника,
	|	НастройкиСинхронизацииФайлов.ПравилоОтбора КАК ПравилоОтбора,
	|	НастройкиСинхронизацииФайлов.ЭтоФайл КАК ЭтоФайл,
	|	НастройкиСинхронизацииФайлов.УчетнаяЗапись КАК УчетнаяЗапись,
	|	НастройкиСинхронизацииФайлов.Синхронизировать КАК Синхронизировать
	|ИЗ
	|	РегистрСведений.НастройкиСинхронизацииФайлов КАК НастройкиСинхронизацииФайлов
	|		ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ИдентификаторыОбъектовМетаданных КАК ИдентификаторыОбъектовМетаданных
	|		ПО (ТИПЗНАЧЕНИЯ(НастройкиСинхронизацииФайлов.ВладелецФайла) = ТИПЗНАЧЕНИЯ(ИдентификаторыОбъектовМетаданных.ЗначениеПустойСсылки))
	|ГДЕ
	|	НастройкиСинхронизацииФайлов.УчетнаяЗапись = &УчетнаяЗапись";
	
	ЗапросНастройкиСинхронизации.УстановитьПараметр("УчетнаяЗапись", УчетнаяЗапись);
	НастройкиСинхронизации = ЗапросНастройкиСинхронизации.Выполнить().Выгрузить();
	
	ТаблицаФайлов = Неопределено;
	
	Для Каждого Настройка Из НастройкиСинхронизации Цикл
		
		СправочникФайлов = ОбщегоНазначения.ОбъектМетаданныхПоИдентификатору(Настройка.ТипВладельцаФайла, Ложь);
		Если ТипЗнч(СправочникФайлов) <> Тип("ОбъектМетаданных") Тогда
			Продолжить;
		КонецЕсли;
		Если НЕ ОбщегоНазначения.ОбъектМетаданныхДоступенПоФункциональнымОпциям(СправочникФайлов) Тогда
			Продолжить;
		КонецЕсли;
		
		ДеревоФайлов = ВыбратьДанныеПоПравилуСинхронизации(Настройка);
		Если ДеревоФайлов = Неопределено Тогда
			Продолжить;
		КонецЕсли;
		
		Если ТаблицаФайлов = Неопределено Тогда
			
			ТаблицаФайлов = Новый ТаблицаЗначений;
			Для Каждого Колонка Из ДеревоФайлов.Колонки Цикл
				ТаблицаФайлов.Колонки.Добавить(Колонка.Имя);
			КонецЦикла;
			
			ТаблицаФайлов.Колонки.Добавить("Синхронизировать", Новый ОписаниеТипов("Число"));
			
		КонецЕсли;
		
		Если Настройка.ЭтоНастройкаДляЭлементаСправочника Тогда
			КорневаяПапка = Настройка.ИдентификаторВладельца;
		Иначе
			КорневаяПапка = Настройка.ВладелецФайла;
		КонецЕсли;
		
		Для Каждого СтрокаФайлов Из ДеревоФайлов.Строки Цикл
			
			НоваяСтрока = ТаблицаФайлов.Добавить();
			НоваяСтрока.Синхронизировать = ?(Настройка.Синхронизировать, 1, 0);
			ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаФайлов);
			
			Если НоваяСтрока.ФайлСсылка = Неопределено Тогда
				НоваяСтрока.ФайлСсылка = КорневаяПапка;
			КонецЕсли;
			
			Если ЗначениеЗаполнено(СтрокаФайлов.ФайлСсылка) Тогда
				
				МетаданныеФайла = СтрокаФайлов.ФайлСсылка.Метаданные();
				Если Метаданные.Справочники.Содержит(МетаданныеФайла)
					И МетаданныеФайла.Иерархический Тогда
				
					РодительФайла = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(СтрокаФайлов.ФайлСсылка, "Родитель");
					Если ЗначениеЗаполнено(РодительФайла) Тогда
						НоваяСтрока.Родитель = РодительФайла;
					КонецЕсли;
					
				КонецЕсли;
				
			КонецЕсли;
			
			Если Не ЗначениеЗаполнено(НоваяСтрока.Родитель) Тогда
				НоваяСтрока.Родитель = КорневаяПапка;
			КонецЕсли;
			
		КонецЦикла;
		
	КонецЦикла;
	
	Если ТаблицаФайлов = Неопределено Тогда
		Возврат ТаблицаФайлов;
	КонецЕсли;
	
	ИменаКолонок = Новый Массив;
	Для Каждого Колонка Из ТаблицаФайлов.Колонки Цикл
		Если Колонка.Имя <> "Синхронизировать" Тогда
			ИменаКолонок.Добавить(Колонка.Имя);
		КонецЕсли;
	КонецЦикла;
	
	КолонкиГруппировки = СтрСоединить(ИменаКолонок, ",");
	ТаблицаФайлов.Свернуть(КолонкиГруппировки, "Синхронизировать");
	ТаблицаФайлов.Сортировать("Синхронизировать" + ?(Не Синхронизировать, " Убыв", ""));
	
	УдаляемыеСтроки = Новый Массив;
	НесинхронизируемыеВладельцы = Новый Массив;
	Для Каждого Строка Из ТаблицаФайлов Цикл
		
		Если (Синхронизировать
			И Строка.Синхронизировать > 0)
			Или (Не Синхронизировать
			И Строка.Синхронизировать = 0) Тогда
			
			Прервать;
			
		КонецЕсли;
		
		УдаляемыеСтроки.Добавить(Строка);
		Если Не Синхронизировать
			И НесинхронизируемыеВладельцы.Найти(Строка.Родитель) = Неопределено Тогда
			
			НесинхронизируемыеВладельцы.Добавить(Строка.Родитель);
			
		КонецЕсли;
		
	КонецЦикла;
	
	Для Каждого Строка Из УдаляемыеСтроки Цикл
		ТаблицаФайлов.Удалить(Строка);
	КонецЦикла;
	
	ТаблицаВладельцев = ТаблицаФайлов.Скопировать(, "Родитель");
	ТаблицаВладельцев.Свернуть("Родитель");
	
	Если Синхронизировать Тогда
		ОбъектыДляСинхронизации = ТаблицаВладельцев.ВыгрузитьКолонку("Родитель");
	Иначе
		
		ОбъектыДляСинхронизации = Новый Массив;
		Для Каждого СтрокаВладельца Из ТаблицаВладельцев Цикл
			
			Если НесинхронизируемыеВладельцы.Найти(СтрокаВладельца.Родитель) = Неопределено Тогда
				ОбъектыДляСинхронизации.Добавить(СтрокаВладельца.Родитель);
			КонецЕсли;
			
		КонецЦикла;
		
	КонецЕсли;
	
	Запрос = Новый Запрос;
	Запрос.Текст = 
	"ВЫБРАТЬ РАЗЛИЧНЫЕ
	|	ВЫБОР
	|		КОГДА ТИПЗНАЧЕНИЯ(НастройкиСинхронизацииФайлов.ВладелецФайла) = ТИП(Справочник.ИдентификаторыОбъектовМетаданных)
	|			ТОГДА НастройкиСинхронизацииФайлов.ВладелецФайла
	|		ИНАЧЕ ИдентификаторыОбъектовМетаданных.Ссылка
	|	КОНЕЦ КАК ФайлСсылка,
	|	НастройкиСинхронизацииФайлов.ЭтоФайл КАК ЭтоФайл,
	|	НастройкиСинхронизацииФайлов.УчетнаяЗапись КАК УчетнаяЗапись,
	|	НастройкиСинхронизацииФайлов.ВладелецФайла КАК ВладелецФайла,
	|	НастройкиСинхронизацииФайлов.ТипВладельцаФайла КАК ТипВладельцаФайла
	|ПОМЕСТИТЬ ВТВиртуальныеКорневыеПапки
	|ИЗ
	|	РегистрСведений.НастройкиСинхронизацииФайлов КАК НастройкиСинхронизацииФайлов
	|		ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ИдентификаторыОбъектовМетаданных КАК ИдентификаторыОбъектовМетаданных
	|		ПО (ТИПЗНАЧЕНИЯ(НастройкиСинхронизацииФайлов.ВладелецФайла) = ТИПЗНАЧЕНИЯ(ИдентификаторыОбъектовМетаданных.ЗначениеПустойСсылки))
	|ГДЕ
	|	НастройкиСинхронизацииФайлов.Синхронизировать = &Синхронизировать
	|	И НастройкиСинхронизацииФайлов.УчетнаяЗапись = &УчетнаяЗапись
	|
	|ИНДЕКСИРОВАТЬ ПО
	|	УчетнаяЗапись
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	ВТВиртуальныеКорневыеПапки.ФайлСсылка КАК ФайлСсылка,
	|	ЕСТЬNULL(ИдентификаторыОбъектовМетаданных.Синоним, """") КАК Синоним,
	|	ВТВиртуальныеКорневыеПапки.ЭтоФайл КАК ЭтоФайл,
	|	ЛОЖЬ КАК ПометкаУдаления,
	|	ИСТИНА КАК ЭтоПапка,
	|	ИСТИНА КАК ЕстьВБазе,
	|	ЛОЖЬ КАК ЕстьНаСервере,
	|	ЛОЖЬ КАК Обработан,
	|	ЛОЖЬ КАК ИзмененНаСервере,
	|	СтатусыСинхронизацииФайловСОблачнымСервисом.Href КАК Href,
	|	СтатусыСинхронизацииФайловСОблачнымСервисом.Etag КАК Etag,
	|	СтатусыСинхронизацииФайловСОблачнымСервисом.УникальныйИдентификатор1С КАК УникальныйИдентификатор1С,
	|	ВТВиртуальныеКорневыеПапки.ВладелецФайла КАК ВладелецФайла,
	|	ВТВиртуальныеКорневыеПапки.ТипВладельцаФайла КАК ТипВладельцаФайла
	|ИЗ
	|	ВТВиртуальныеКорневыеПапки КАК ВТВиртуальныеКорневыеПапки
	|		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.СтатусыСинхронизацииФайловСОблачнымСервисом КАК СтатусыСинхронизацииФайловСОблачнымСервисом
	|		ПО ВТВиртуальныеКорневыеПапки.УчетнаяЗапись = СтатусыСинхронизацииФайловСОблачнымСервисом.УчетнаяЗапись
	|			И ВТВиртуальныеКорневыеПапки.ФайлСсылка = СтатусыСинхронизацииФайловСОблачнымСервисом.Файл
	|		ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ИдентификаторыОбъектовМетаданных КАК ИдентификаторыОбъектовМетаданных
	|			ПО ВТВиртуальныеКорневыеПапки.ФайлСсылка = ИдентификаторыОбъектовМетаданных.Ссылка 
	|ГДЕ
	|	ВТВиртуальныеКорневыеПапки.ФайлСсылка В (&ВладельцыФайлов)
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|УНИЧТОЖИТЬ ВТВиртуальныеКорневыеПапки";
		
	Запрос.УстановитьПараметр("УчетнаяЗапись", УчетнаяЗапись);
	Запрос.УстановитьПараметр("Синхронизировать", Синхронизировать);
	Запрос.УстановитьПараметр("ВладельцыФайлов", ОбъектыДляСинхронизации);
	РезультатЗапроса = Запрос.Выполнить();
	
	ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
	
	МассивВиртуальныхПапок = Новый Массив;
	
	Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
		Если МассивВиртуальныхПапок.Найти(ВыборкаДетальныеЗаписи.ФайлСсылка) <> Неопределено Тогда
			Продолжить;
		КонецЕсли;
		МассивВиртуальныхПапок.Добавить(ВыборкаДетальныеЗаписи.ФайлСсылка);
		СтрокаВиртуальнойКорневойПапки = ТаблицаФайлов.Добавить();
		ЗаполнитьЗначенияСвойств(СтрокаВиртуальнойКорневойПапки, ВыборкаДетальныеЗаписи);
		СтрокаВиртуальнойКорневойПапки.Наименование = СтрЗаменить(ВыборкаДетальныеЗаписи.Синоним, ":", "");
	КонецЦикла;
	
	Возврат ТаблицаФайлов;
	
КонецФункции

Функция ВыбратьДанныеПоПравилуСинхронизации(НастройкаСинхронизации)
	
	КомпоновщикНастроек = Новый КомпоновщикНастроекКомпоновкиДанных;
	
	НастройкиКомпоновщика = НастройкаСинхронизации.ПравилоОтбора.Получить();
	Если НастройкиКомпоновщика <> Неопределено Тогда
		КомпоновщикНастроек.ЗагрузитьНастройки(НастройкаСинхронизации.ПравилоОтбора.Получить());
	КонецЕсли;
	
	СхемаКомпоновкиДанных = Новый СхемаКомпоновкиДанных;
	ИсточникДанных = СхемаКомпоновкиДанных.ИсточникиДанных.Добавить();
	ИсточникДанных.Имя = "ИсточникДанных1";
	ИсточникДанных.ТипИсточникаДанных = "Local";
	
	НаборДанных = СхемаКомпоновкиДанных.НаборыДанных.Добавить(Тип("НаборДанныхЗапросСхемыКомпоновкиДанных"));
	НаборДанных.Имя = "НаборДанных1";
	НаборДанных.ИсточникДанных = ИсточникДанных.Имя;
	
	СхемаКомпоновкиДанных.ПоляИтога.Очистить();
	
	Если НастройкаСинхронизации.ЭтоНастройкаДляЭлементаСправочника Тогда
		ВладелецФайла = НастройкаСинхронизации.ИдентификаторВладельца;
		ЭлементИсключение = НастройкаСинхронизации.ВладелецФайла;
	Иначе
		ВладелецФайла = НастройкаСинхронизации.ВладелецФайла;
		ЭлементИсключение = Неопределено;
	КонецЕсли;
	
	МассивИсключений = Новый Массив;
	ТекстЗапроса = ТекстЗапросаДляСинхронизацииФайлов(ВладелецФайла, НастройкаСинхронизации, МассивИсключений, 
		ЭлементИсключение);
	Если ПустаяСтрока(ТекстЗапроса) Тогда
		Возврат Неопределено;
	КонецЕсли;
			
	СхемаКомпоновкиДанных.НаборыДанных[0].Запрос = ТекстЗапроса;
		
	Структура = КомпоновщикНастроек.Настройки.Структура.Добавить(Тип("ГруппировкаКомпоновкиДанных"));
	
	ВыбранноеПоле = Структура.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
	ВыбранноеПоле.Поле = Новый ПолеКомпоновкиДанных("ФайлСсылка");
	
	ВыбранноеПоле = Структура.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
	ВыбранноеПоле.Поле = Новый ПолеКомпоновкиДанных("Наименование");
	
	ВыбранноеПоле = Структура.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
	ВыбранноеПоле.Поле = Новый ПолеКомпоновкиДанных("Расширение");
	
	ВыбранноеПоле = Структура.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
	ВыбранноеПоле.Поле = Новый ПолеКомпоновкиДанных("ПометкаУдаления");
	
	ВыбранноеПоле = Структура.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
	ВыбранноеПоле.Поле = Новый ПолеКомпоновкиДанных("Родитель");
	
	ВыбранноеПоле = Структура.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
	ВыбранноеПоле.Поле = Новый ПолеКомпоновкиДанных("ЭтоПапка");
	
	ВыбранноеПоле = Структура.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
	ВыбранноеПоле.Поле = Новый ПолеКомпоновкиДанных("ЕстьВБазе");
	
	ВыбранноеПоле = Структура.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
	ВыбранноеПоле.Поле = Новый ПолеКомпоновкиДанных("ЕстьНаСервере");
	
	ВыбранноеПоле = Структура.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
	ВыбранноеПоле.Поле = Новый ПолеКомпоновкиДанных("Изменения");
	
	ВыбранноеПоле = Структура.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
	ВыбранноеПоле.Поле = Новый ПолеКомпоновкиДанных("Href");
	
	ВыбранноеПоле = Структура.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
	ВыбранноеПоле.Поле = Новый ПолеКомпоновкиДанных("Etag");
	
	ВыбранноеПоле = Структура.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
	ВыбранноеПоле.Поле = Новый ПолеКомпоновкиДанных("Обработан");
	
	ВыбранноеПоле = Структура.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
	ВыбранноеПоле.Поле = Новый ПолеКомпоновкиДанных("ДатаСинхронизации");
	
	ВыбранноеПоле = Структура.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
	ВыбранноеПоле.Поле = Новый ПолеКомпоновкиДанных("UID1C");
	
	ВыбранноеПоле = Структура.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
	ВыбранноеПоле.Поле = Новый ПолеКомпоновкиДанных("ToHref");
	
	ВыбранноеПоле = Структура.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
	ВыбранноеПоле.Поле = Новый ПолеКомпоновкиДанных("ToEtag");
	
	ВыбранноеПоле = Структура.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
	ВыбранноеПоле.Поле = Новый ПолеКомпоновкиДанных("РодительСервер");
	
	ВыбранноеПоле = Структура.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
	ВыбранноеПоле.Поле = Новый ПолеКомпоновкиДанных("НаименованиеСервер");
	
	ВыбранноеПоле = Структура.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
	ВыбранноеПоле.Поле = Новый ПолеКомпоновкиДанных("ИзмененНаСервере");
	
	ВыбранноеПоле = Структура.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
	ВыбранноеПоле.Поле = Новый ПолеКомпоновкиДанных("ЗашифрованНаСервере");
	
	ВыбранноеПоле = Структура.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
	ВыбранноеПоле.Поле = Новый ПолеКомпоновкиДанных("Уровень");
	
	ВыбранноеПоле = Структура.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
	ВыбранноеПоле.Поле = Новый ПолеКомпоновкиДанных("РодительСортировка");
	
	ВыбранноеПоле = Структура.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
	ВыбранноеПоле.Поле = Новый ПолеКомпоновкиДанных("ЭтоФайл");
	
	ВыбранноеПоле = Структура.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
	ВыбранноеПоле.Поле = Новый ПолеКомпоновкиДанных("ПодписанЭП");
	
	ВыбранноеПоле = Структура.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
	ВыбранноеПоле.Поле = Новый ПолеКомпоновкиДанных("Зашифрован");
	
	КомпоновщикНастроек.Инициализировать(Новый ИсточникДоступныхНастроекКомпоновкиДанных(СхемаКомпоновкиДанных));
	
	Параметр = КомпоновщикНастроек.Настройки.ПараметрыДанных.Элементы.Найти("УчетнаяЗапись");
	Параметр.Значение = НастройкаСинхронизации.УчетнаяЗапись;
	Параметр.Использование = Истина;
	
	Параметр = КомпоновщикНастроек.Настройки.ПараметрыДанных.Элементы.Найти("ТипВладельца");
	Параметр.Значение = ТипЗнч(ВладелецФайла.ЗначениеПустойСсылки);
	Параметр.Использование = Истина;
	
	Если МассивИсключений.Количество() > 0 Тогда
		Параметр = КомпоновщикНастроек.Настройки.ПараметрыДанных.Элементы.Найти("МассивИсключений");
		Параметр.Значение = МассивИсключений;
		Параметр.Использование = Истина;
	КонецЕсли;
	
	Если НастройкаСинхронизации.ЭтоНастройкаДляЭлементаСправочника Тогда
		Параметр = КомпоновщикНастроек.Настройки.ПараметрыДанных.Элементы.Найти("ЭлементИсключение");
		Параметр.Значение = ЭлементИсключение;
		Параметр.Использование = Истина;
	КонецЕсли;
	
	КомпоновщикМакета         = Новый КомпоновщикМакетаКомпоновкиДанных;
	ПроцессорКомпоновкиДанных = Новый ПроцессорКомпоновкиДанных;
	ПроцессорВывода           = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
	ДеревоЗначений            = Новый ДеревоЗначений;
	
	МакетКомпоновкиДанных = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, КомпоновщикНастроек.Настройки,
		, , Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений"));
	ПроцессорКомпоновкиДанных.Инициализировать(МакетКомпоновкиДанных);
	ПроцессорВывода.УстановитьОбъект(ДеревоЗначений);
	ПроцессорВывода.Вывести(ПроцессорКомпоновкиДанных);
	
	Возврат ДеревоЗначений;
	
КонецФункции

// Параметры:
//   УчетнаяЗапись - СправочникСсылка.УчетныеЗаписиСинхронизацииФайлов
//
Процедура ВыполнитьПроверкуПодключения(УчетнаяЗапись, РезультатПроверки) Экспорт 

	РезультатПроверки = Новый Структура("РезультатТекст, РезультатПротокол, Отказ, КодОшибки","","",Ложь);
	
	ПараметрыСинхронизации = СвойстваСинхронизацииФайлов(УчетнаяЗапись);
	
	АдресСервера = КодироватьURIПоСтруктуре(ПараметрыСинхронизации.СтруктураАдресаСервера);
	
	НаименованиеУчетнойЗаписи = Строка(УчетнаяЗапись);
	
	ТекстСобытия = НСтр("ru = 'Начата проверка синхронизации файлов'") + " " + НаименованиеУчетнойЗаписи;
	ЗаписатьВЖурналСобытийСинхронизацииФайлов(ТекстСобытия, ПараметрыСинхронизации.УчетнаяЗапись);
	
	ПрочитатьПараметрыКаталога(РезультатПроверки, АдресСервера, ПараметрыСинхронизации);
	ПроверитьВозможностьРазмещенияФайлов(РезультатПроверки, АдресСервера, ПараметрыСинхронизации);
	
	ТекстСобытия = НСтр("ru = 'Завершена проверка синхронизации файлов'") + " " + НаименованиеУчетнойЗаписи;
	ЗаписатьВЖурналСобытийСинхронизацииФайлов(ТекстСобытия, ПараметрыСинхронизации.УчетнаяЗапись);

КонецПроцедуры

Процедура ОсвободитьЗахваченныеФайлыФон(ПараметрыВызова, АдресВХранилище) Экспорт
	УдалитьНесинхронизируемыеФайлы();
КонецПроцедуры

Процедура УдалитьНесинхронизируемыеФайлы()
	
	Запрос = Новый Запрос;
	Запрос.Текст = 
	"ВЫБРАТЬ
	|	УчетныеЗаписиСинхронизацииФайлов.Ссылка КАК УчетнаяЗапись
	|ИЗ
	|	Справочник.УчетныеЗаписиСинхронизацииФайлов КАК УчетныеЗаписиСинхронизацииФайлов
	|		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.НастройкиСинхронизацииФайлов КАК НастройкиСинхронизацииФайлов
	|		ПО (НастройкиСинхронизацииФайлов.УчетнаяЗапись = УчетныеЗаписиСинхронизацииФайлов.Ссылка)
	|		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.СтатусыСинхронизацииФайловСОблачнымСервисом КАК СтатусыСинхронизацииФайловСОблачнымСервисом
	|		ПО (СтатусыСинхронизацииФайловСОблачнымСервисом.УчетнаяЗапись = УчетныеЗаписиСинхронизацииФайлов.Ссылка)
	|ГДЕ
	|	(УчетныеЗаписиСинхронизацииФайлов.ПометкаУдаления
	|			ИЛИ НастройкиСинхронизацииФайлов.Синхронизировать <> ИСТИНА)
	|	И СтатусыСинхронизацииФайловСОблачнымСервисом.УчетнаяЗапись ЕСТЬ НЕ NULL 
	|
	|СГРУППИРОВАТЬ ПО
	|	УчетныеЗаписиСинхронизацииФайлов.Ссылка";
	ВыборкаУчетнаяЗапись = Запрос.Выполнить().Выбрать();
	Пока ВыборкаУчетнаяЗапись.Следующий() Цикл
		// @skip-check query-in-loop - Порционная обработка большого объема данных.
		УдалитьНесинхронизируемыеФайлыУчетнойЗаписи(ВыборкаУчетнаяЗапись.УчетнаяЗапись);
	КонецЦикла;
	
КонецПроцедуры

// Освобождение файлов, захваченных учетными записями с пометкой на удаление или с отключенными настройками синхронизации.
//
Процедура УдалитьНесинхронизируемыеФайлыУчетнойЗаписи(УчетнаяЗапись)
	
	ПараметрыСинхронизации = СвойстваСинхронизацииФайлов(УчетнаяЗапись);
	Если ПараметрыСинхронизации = Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	АдресСервера = КодироватьURIПоСтруктуре(ПараметрыСинхронизации.СтруктураАдресаСервера);
	
	ТекстСобытия = НСтр("ru = 'Начало освобождения файлов, захваченных облачным сервисом.'");
	ЗаписатьВЖурналСобытийСинхронизацииФайлов(ТекстСобытия, ПараметрыСинхронизации.УчетнаяЗапись);
	
	ДеревоФайловСервера = СформироватьСтруктуруДереваФайловСервера();
		
	Попытка
		
		Отказ = Ложь;
		СкачатьДеревоФайловРекурсивно(ДеревоФайловСервера.Строки, АдресСервера, ПараметрыСинхронизации, Отказ);
		Если Отказ = Истина Тогда
			ТекстОшибки = НСтр("ru = 'Не удалось загрузить структуру файлов с облачного сервиса, синхронизация не выполнена.'");
			ВызватьИсключение ТекстОшибки;
		КонецЕсли;
		
		// Сравниваем с деревом файлов в системе, синхронизация идет по уникальному идентификатору.
		ТаблицаФайлов = ВыбратьДанныеПоПравилам(УчетнаяЗапись, Ложь);
		Если ТаблицаФайлов <> Неопределено Тогда
		
			ВычислитьУровеньРекурсивно(ТаблицаФайлов);
			ТаблицаФайлов.Сортировать("ЭтоПапка ВОЗР, Уровень УБЫВ, РодительСортировка УБЫВ");
			// Перебираем таблицу и решаем что делать с файлами и папками.
			Для Каждого СтрокаТаблицы Из ТаблицаФайлов Цикл
				
				Если СтрокаТаблицы.Обработан Тогда
					Продолжить;
				КонецЕсли;
				
				НачатьТранзакцию();
				Попытка
					
					БлокировкаДанных = Новый БлокировкаДанных();
					Если Не СтрокаТаблицы.ЭтоПапка Тогда
						ЭлементБлокировки = БлокировкаДанных.Добавить(Метаданные.НайтиПоТипу(ТипЗнч(СтрокаТаблицы.ФайлСсылка)).ПолноеИмя());
						ЭлементБлокировки.УстановитьЗначение("Ссылка", СтрокаТаблицы.ФайлСсылка);
					КонецЕсли;
					ЗаблокироватьСтатусСинхронизацииФайла(БлокировкаДанных, СтрокаТаблицы.ФайлСсылка, УчетнаяЗапись);
					БлокировкаДанных.Заблокировать();
					
					Если ЗначениеЗаполнено(СтрокаТаблицы.Href) Тогда
						ВызватьМетодDELETE(СтрокаТаблицы.Href, ПараметрыСинхронизации);
						ТекстСобытия = НСтр("ru = 'Удален объект в облачном сервисе %1'");
						ЗаписатьВЖурналСобытийСинхронизацииФайлов(СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
								ТекстСобытия, СтрокаТаблицы.НаименованиеСервер), 
							ПараметрыСинхронизации.УчетнаяЗапись);
							
						Если Не СтрокаТаблицы.ЭтоПапка И СтрокаТаблицы.ПодписанЭП Тогда
							УдалитьНесинхронизируемыеПодписи(СтрокаТаблицы, ДеревоФайловСервера.Строки, ПараметрыСинхронизации);
						КонецЕсли;
					КонецЕсли;

					СтрокаТаблицы.РодительСервер = Неопределено;
					СтрокаТаблицы.НаименованиеСервер = "";
					СтрокаТаблицы.ЕстьНаСервере = Ложь;
					СтрокаТаблицы.Обработан = Истина;
					
					Если Не СтрокаТаблицы.ЭтоПапка Тогда
						ОсвободитьФайл(СтрокаТаблицы.ФайлСсылка);
					КонецЕсли;
					
					// Удаляем последний Href, чтобы повторно не идентифицировать.
					УдалитьСтатусСинхронизацииФайла(СтрокаТаблицы.ФайлСсылка, УчетнаяЗапись);
					ЗафиксироватьТранзакцию();
					
				Исключение
					ОтменитьТранзакцию();
					ТекстСобытия = НСтр("ru = 'Объект не был удален в облачном сервисе %1 по причине:
						|%2'");
					ЗаписатьВЖурналСобытийСинхронизацииФайлов(СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
						ТекстСобытия, СтрокаТаблицы.НаименованиеСервер, ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке())),
						ПараметрыСинхронизации.УчетнаяЗапись, УровеньЖурналаРегистрации.Ошибка);
				КонецПопытки;
				
			КонецЦикла;
		КонецЕсли;
		
	Исключение
		ЗаписатьВЖурналСобытийСинхронизацииФайлов(ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()), 
			ПараметрыСинхронизации.УчетнаяЗапись, УровеньЖурналаРегистрации.Ошибка);
	КонецПопытки;
	
	ТекстСобытия = НСтр("ru = 'Завершено освобождения файлов, захваченных облачным сервисом.'");
	ЗаписатьВЖурналСобытийСинхронизацииФайлов(ТекстСобытия, ПараметрыСинхронизации.УчетнаяЗапись);
	
КонецПроцедуры

Процедура УдалитьНесинхронизируемыеПодписи(СтрокаТаблицы, ДеревоФайловСервераСтроки, ПараметрыСинхронизации)
		
	Отбор = Новый Структура("Etag, ИмяФайла", СтрокаТаблицы.Etag);
	Отбор.ИмяФайла = ?(СтрокаТаблицы.Зашифрован, СтрокаТаблицы.Наименование + ".p7m", СтрокаТаблицы.Наименование);
	
	СтрокаФайла = ДеревоФайловСервераСтроки.НайтиСтроки(Отбор, Истина);

	Если СтрокаФайла.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	UID1C = СтрокаФайла[0].UID1C;
	
	Если Не ЗначениеЗаполнено(UID1C) Тогда
		Возврат;
	КонецЕсли;
	
	ПапкаФайла = СтрокаФайла[0].Родитель;
	
	Для Каждого СтрокаДерева Из ПапкаФайла.Строки Цикл
		Если СтрНачинаетсяС(СтрокаДерева.UID1C, UID1C)
			И СтрЗаканчиваетсяНа(СтрокаДерева.UID1C, "p7s") Тогда
			
			ВызватьМетодDELETE(СтрокаДерева.Href, ПараметрыСинхронизации);
			ТекстСобытия = НСтр("ru = 'Удалена подпись %1 объекта %2 в облачном сервисе'");
			ЗаписатьВЖурналСобытийСинхронизацииФайлов(СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				ТекстСобытия, СтрокаДерева.ИмяФайла, СтрокаТаблицы.Наименование),
				ПараметрыСинхронизации.УчетнаяЗапись);
		КонецЕсли;
	КонецЦикла;

КонецПроцедуры

Функция ДанныеОтбораЖурналаРегистрации(УчетнаяЗапись) Экспорт
	
	Отбор = Новый Структура;
	Отбор.Вставить("СобытиеЖурналаРегистрации ", СобытиеЖурналаРегистрацииСинхронизация());
	
	Запрос = Новый Запрос;
	Запрос.Текст = "ВЫБРАТЬ
	|	СтатусыСинхронизацииФайловСОблачнымСервисом.НомерСеанса КАК НомерСеанса,
	|	СтатусыСинхронизацииФайловСОблачнымСервисом.ДатаСинхронизацииНачало КАК ДатаСинхронизацииНачало,
	|	СтатусыСинхронизацииФайловСОблачнымСервисом.ДатаСинхронизацииЗавершение КАК ДатаСинхронизацииЗавершение
	|ИЗ
	|	РегистрСведений.СтатусыСинхронизацииФайловСОблачнымСервисом КАК СтатусыСинхронизацииФайловСОблачнымСервисом
	|ГДЕ
	|	СтатусыСинхронизацииФайловСОблачнымСервисом.Файл = """"
	|	И СтатусыСинхронизацииФайловСОблачнымСервисом.УчетнаяЗапись = &УчетнаяЗапись";
	
	Запрос.УстановитьПараметр("УчетнаяЗапись", УчетнаяЗапись);
	
	РезультатЗапроса = Запрос.Выполнить().Выбрать();
	
	Если РезультатЗапроса.Следующий() Тогда
		
		СписокСеансов = Новый СписокЗначений;
		СписокСеансов.Добавить(РезультатЗапроса.НомерСеанса);
		
		Отбор.Вставить("Данные                    ", УчетнаяЗапись);
		Отбор.Вставить("ДатаНачала",                 РезультатЗапроса.ДатаСинхронизацииНачало);
		Отбор.Вставить("ДатаОкончания",              РезультатЗапроса.ДатаСинхронизацииЗавершение);
		Отбор.Вставить("Сеанс",                      СписокСеансов);
	
	КонецЕсли;
	
	Возврат Отбор;
	
КонецФункции

Функция СведенияОСинхронизации(ВладелецФайла = Неопределено) Экспорт
	
	Запрос = Новый Запрос;
	Запрос.Текст = "ВЫБРАТЬ ПЕРВЫЕ 1
		|	СтатусыСинхронизацииФайловСОблачнымСервисом.УчетнаяЗапись КАК УчетнаяЗапись,
		|	СтатусыСинхронизацииФайловСОблачнымСервисом.ДатаСинхронизацииНачало КАК ДатаСинхронизации,
		|	СтатусыСинхронизацииФайловСОблачнымСервисом.НомерСеанса КАК НомерСеанса,
		|	СтатусыСинхронизацииФайловСОблачнымСервисом.Синхронизирован КАК Синхронизирован,
		|	СтатусыСинхронизацииФайловСОблачнымСервисом.ДатаСинхронизацииЗавершение КАК ДатаСинхронизацииЗавершение,
		|	СтатусыСинхронизацииФайловСОблачнымСервисом.Href КАК Href,
		|	СтатусыСинхронизацииФайловСОблачнымСервисом.УчетнаяЗапись.Наименование КАК УчетнаяЗаписьНаименование,
		|	СтатусыСинхронизацииФайловСОблачнымСервисом.УчетнаяЗапись.Сервис КАК Сервис
		|ИЗ
		|	РегистрСведений.СтатусыСинхронизацииФайловСОблачнымСервисом КАК СтатусыСинхронизацииФайловСОблачнымСервисом
		|		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.СтатусыСинхронизацииФайловСОблачнымСервисом КАК СтатусыСинхронизацииФайловСОблачнымСервисомКорень
		|		ПО СтатусыСинхронизацииФайловСОблачнымСервисом.УчетнаяЗапись = СтатусыСинхронизацииФайловСОблачнымСервисомКорень.УчетнаяЗапись
		|			И (СтатусыСинхронизацииФайловСОблачнымСервисомКорень.Файл = """""""")
		|ГДЕ
		|	СтатусыСинхронизацииФайловСОблачнымСервисом.Файл = &Файл";
	
	Запрос.УстановитьПараметр("Файл", ВладелецФайла);
	Таблица = Запрос.Выполнить().Выгрузить();
	
	Пока Таблица.Количество() > 0  Цикл
		Результат = ОбщегоНазначения.СтрокаТаблицыЗначенийВСтруктуру(Таблица[0]);
		Возврат Результат;
	КонецЦикла;
	
	Возврат Новый Структура();
	
КонецФункции

// Возвращает признак принадлежности узла к плану обмена РИБ.
//
// Параметры:
//  УзелИнформационнойБазы - ПланОбменаСсылка - узел плана обмена, для которого требуется получить значение функции.
//
// Возвращаемое значение:
//    Булево - Истина - узел принадлежит плану обмена РИБ, иначе Ложь.
//
Функция ЭтоУзелРаспределеннойИнформационнойБазы(Знач УзелИнформационнойБазы)
	
	Возврат РаботаСФайламиСлужебныйПовтИсп.ЭтоУзелРаспределеннойИнформационнойБазы(
		УзелИнформационнойБазы.Метаданные().ПолноеИмя());
	
КонецФункции

Функция ЭтоКодСостоянияОшибки(КодСостояния)
	Возврат (КодСостояния >= 400 И КодСостояния <= 599) Или КодСостояния = 0; 
КонецФункции

#КонецОбласти

#Область ИзвлечениеТекста

Функция ТекстЗапросаДляФайловСНеизвлеченнымТекстом(ИмяСправочника, ЧислоФайловВВыборке,
	ПолучитьВсеФайлы, ДополнительныеПоля)
	
	Если ДополнительныеПоля Тогда
		ТекстЗапроса =
		"ВЫБРАТЬ ПЕРВЫЕ 1
		|	Файлы.Ссылка КАК Ссылка,
		|	ЕСТЬNULL(РегистрСведенийКодировкиФайлов.Кодировка, """") КАК Кодировка,
		|	Файлы.Расширение КАК Расширение,
		|	Файлы.Наименование КАК Наименование
		|ИЗ
		|	&ИмяСправочника КАК Файлы
		|		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КодировкиФайлов КАК РегистрСведенийКодировкиФайлов
		|		ПО (РегистрСведенийКодировкиФайлов.Файл = Файлы.Ссылка)
		|ГДЕ
		|	Файлы.СтатусИзвлеченияТекста В (
		|		ЗНАЧЕНИЕ(Перечисление.СтатусыИзвлеченияТекстаФайлов.НеИзвлечен),
		|		ЗНАЧЕНИЕ(Перечисление.СтатусыИзвлеченияТекстаФайлов.ПустаяСсылка))";
	Иначе
		ТекстЗапроса =
		"ВЫБРАТЬ ПЕРВЫЕ 1
		|	Файлы.Ссылка КАК Ссылка,
		|	ЕСТЬNULL(РегистрСведенийКодировкиФайлов.Кодировка, """") КАК Кодировка
		|ИЗ
		|	&ИмяСправочника КАК Файлы
		|		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КодировкиФайлов КАК РегистрСведенийКодировкиФайлов
		|		ПО (РегистрСведенийКодировкиФайлов.Файл = Файлы.Ссылка)
		|ГДЕ
		|	Файлы.СтатусИзвлеченияТекста В (
		|		ЗНАЧЕНИЕ(Перечисление.СтатусыИзвлеченияТекстаФайлов.НеИзвлечен),
		|		ЗНАЧЕНИЕ(Перечисление.СтатусыИзвлеченияТекстаФайлов.ПустаяСсылка))";
	КонецЕсли;
	
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ЭлектроннаяПодпись") Тогда
		Если ИмяСправочника = "ВерсииФайлов" Тогда
			ТекстЗапроса = ТекстЗапроса + "
				|	И НЕ Файлы.Владелец.Зашифрован"; // @query-part
		Иначе
			ТекстЗапроса = ТекстЗапроса + "
				|	И НЕ Файлы.Зашифрован"; // @query-part
		КонецЕсли;
	КонецЕсли;
	
	СтрокаЗамены = ?(ПолучитьВсеФайлы, "ВЫБРАТЬ", // @query-part
		"ВЫБРАТЬ ПЕРВЫЕ " + Формат(ЧислоФайловВВыборке, "ЧГ=; ЧН=")); // @query-part
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "ВЫБРАТЬ ПЕРВЫЕ 1", СтрокаЗамены); // @query-part
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ИмяСправочника", "Справочник." + ИмяСправочника);
	Возврат ТекстЗапроса;
	
КонецФункции

// Получает полный путь к файлу.
//
// Параметры:
//   ОбъектСсылка - ОпределяемыйТип.ПрисоединенныйФайлОбъект - элемент справочника с файлами,
//                для которого необходимо получить путь к файлу.
//
// Возвращаемое значение:
//   Строка
//
Функция ИмяФайлаСДвоичнымиДанными(ОбъектСсылка) 
	
	ПолноеИмяФайла = "";
	
	Если ОбъектСсылка.ТипХраненияФайла = Перечисления.ТипыХраненияФайлов.ВИнформационнойБазе Тогда
		
		ХранилищеФайла = РаботаСФайлами.ХранилищеФайлаИзИнформационнойБазы(ОбъектСсылка);
		Если ХранилищеФайла = Неопределено Тогда
			Возврат "";
		КонецЕсли;
		
		ДвоичныеДанныеФайла = ХранилищеФайла.Получить(); //ДвоичныеДанные
		Если ТипЗнч(ДвоичныеДанныеФайла) <> Тип("ДвоичныеДанные") Тогда
			Возврат "";
		КонецЕсли;
		
		ПолноеИмяФайла = ПолучитьИмяВременногоФайла(ОбъектСсылка.Расширение);
		ДвоичныеДанныеФайла.Записать(ПолноеИмяФайла);
		
	Иначе
		
		СвойстваФайла = РаботаСФайламиВТомахСлужебный.СвойстваФайлаВТоме();
		ЗаполнитьЗначенияСвойств(СвойстваФайла, ОбъектСсылка);
		
		ПолноеИмяФайла = РаботаСФайламиВТомахСлужебный.ПолноеИмяФайлаВТоме(СвойстваФайла);
		
	КонецЕсли;
	
	Возврат ПолноеИмяФайла;
	
КонецФункции

// Записывает извлеченный текст.
//
// Параметры:
//  ТекущаяВерсия  - СправочникСсылка.ВерсииФайлов - версия файла.
//
Процедура ПриЗаписиИзвлеченногоТекста(ТекущаяВерсия, ФайлЗаблокирован = Истина)
	
	// Если это не версия, то записываем.
	Если ОбщегоНазначения.ЕстьРеквизитОбъекта("ВладелецФайла", Метаданные.НайтиПоТипу(ТипЗнч(ТекущаяВерсия))) Тогда
		ОбновлениеИнформационнойБазы.ЗаписатьДанные(ТекущаяВерсия);
		Возврат;
	КонецЕсли;
	
	Файл = ТекущаяВерсия.Владелец;
	ТекущаяВерсияФайла = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Файл, "ТекущаяВерсия");
	Если ТекущаяВерсияФайла = ТекущаяВерсия.Ссылка Тогда
		Попытка
			ЗаблокироватьДанныеДляРедактирования(Файл);
		Исключение
			ФайлЗаблокирован = Ложь;
			ВызватьИсключение;
		КонецПопытки;
	КонецЕсли;
	
	ОбновлениеИнформационнойБазы.ЗаписатьДанные(ТекущаяВерсия);
	
	Если ТекущаяВерсияФайла = ТекущаяВерсия.Ссылка Тогда
		ФайлОбъект = Файл.ПолучитьОбъект();
		ФайлОбъект.ТекстХранилище = ТекущаяВерсия.ТекстХранилище;
		ОбновлениеИнформационнойБазы.ЗаписатьДанные(ФайлОбъект);
	КонецЕсли;
	
КонецПроцедуры

// Извлекает текст из временного хранилища или из двоичных данных, возвращает статус извлечения.
//
// Параметры:
//   АдресВременногоХранилищаТекста - Строка
//   ДвоичныеДанные                 - ДвоичныеДанные
//                                  - Неопределено
//   Расширение                     - Строка
//                                  - Неопределено
//
Функция ИзвлечьТекст(Знач АдресВременногоХранилищаТекста, Знач ДвоичныеДанные = Неопределено, Знач Расширение = Неопределено) Экспорт
	
	УстановитьОтключениеБезопасногоРежима(Истина);
	
	Результат = Новый Структура("СтатусИзвлеченияТекста, ТекстХранилище");
	
	Если ЭтоАдресВременногоХранилища(АдресВременногоХранилищаТекста) Тогда
		ИзвлеченныйТекст = СтрокаИзВременногоХранилища(АдресВременногоХранилищаТекста);
		Результат.СтатусИзвлеченияТекста = Перечисления.СтатусыИзвлеченияТекстаФайлов.Извлечен;
		Результат.ТекстХранилище = Новый ХранилищеЗначения(ИзвлеченныйТекст, Новый СжатиеДанных(9));
		Возврат Результат;
	КонецЕсли;
		
	Если ИзвлекатьТекстыФайловНаСервере() Тогда
		Результат.СтатусИзвлеченияТекста = Перечисления.СтатусыИзвлеченияТекстаФайлов.НеИзвлечен;
		Результат.ТекстХранилище = Новый ХранилищеЗначения("");
		Возврат Результат; // Текст будет извлечен позднее в регламентном задании.
	КонецЕсли;
	
	Если Не ОбщегоНазначения.ЭтоWindowsСервер() Или ДвоичныеДанные = Неопределено Тогда
		Результат.СтатусИзвлеченияТекста = Перечисления.СтатусыИзвлеченияТекстаФайлов.НеИзвлечен;
		Результат.ТекстХранилище = Новый ХранилищеЗначения("");
		Возврат Результат;
	КонецЕсли;
	
	// Текст извлекается сразу, а не в регламентном задании.
	ИмяВременногоФайла = ПолучитьИмяВременногоФайла(Расширение);
	ДвоичныеДанные.Записать(ИмяВременногоФайла);
	Результат = ИзвлечьТекстИзФайлаНаДиске(ИмяВременногоФайла);
	Попытка
		УдалитьФайлы(ИмяВременногоФайла);
	Исключение
		ЗаписьЖурналаРегистрации(НСтр("ru = 'Файлы.Извлечение текста'",	ОбщегоНазначения.КодОсновногоЯзыка()),
			УровеньЖурналаРегистрации.Ошибка,,, ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
	КонецПопытки;
	
	Возврат Результат;
	
КонецФункции

Функция ИзвлечьТекстИзФайлаНаДиске(Знач ИмяФайла, Знач Кодировка = Неопределено) Экспорт
	
	ИзвлеченныйТекст = "";
	Результат = Новый Структура("СтатусИзвлеченияТекста, ТекстХранилище");
	Результат.СтатусИзвлеченияТекста = Перечисления.СтатусыИзвлеченияТекстаФайлов.ИзвлечьНеУдалось;
	
	Попытка
		Файл = Новый Файл(ИмяФайла);
		Если Не Файл.Существует() Тогда
			Возврат Результат;
		КонецЕсли;
	Исключение
		Возврат Результат;
	КонецПопытки;
	
	Отказ = Ложь;
	ОбщиеНастройки = РаботаСФайламиСлужебныйПовтИсп.НастройкиРаботыСФайлами().ОбщиеНастройки;
	
	РасширениеИмениФайла =
		ОбщегоНазначенияКлиентСервер.ПолучитьРасширениеИмениФайла(ИмяФайла);
	
	РасширениеФайлаВСписке = РаботаСФайламиСлужебныйКлиентСервер.РасширениеФайлаВСписке(
		ОбщиеНастройки.СписокРасширенийТекстовыхФайлов, РасширениеИмениФайла);
	
	Если РасширениеФайлаВСписке Тогда
		
		ИзвлеченныйТекст = РаботаСФайламиСлужебныйКлиентСервер.ИзвлечьТекстИзТекстовогоФайла(
			ИмяФайла, Кодировка, Отказ);
			
	Иначе
	
		Попытка
			Извлечение = Новый ИзвлечениеТекста(ИмяФайла);
			ИзвлеченныйТекст = Извлечение.ПолучитьТекст();
		Исключение
			// Когда текст некому извлечь исключение не требуется. Это нормальный случай.
			ИзвлеченныйТекст = "";
			Отказ = Истина;
		КонецПопытки;
		
		Если ПустаяСтрока(ИзвлеченныйТекст) Тогда
			
			РасширениеИмениФайла =
				ОбщегоНазначенияКлиентСервер.ПолучитьРасширениеИмениФайла(ИмяФайла);
			
			РасширениеФайлаВСписке = РаботаСФайламиСлужебныйКлиентСервер.РасширениеФайлаВСписке(
				ОбщиеНастройки.СписокРасширенийФайловOpenDocument, РасширениеИмениФайла);
			
			Если РасширениеФайлаВСписке Тогда
				ИзвлеченныйТекст = РаботаСФайламиСлужебныйКлиентСервер.ИзвлечьТекстOpenDocument(ИмяФайла, Отказ);
			КонецЕсли;
			
		КонецЕсли;
		
	КонецЕсли;
	
	Если Не Отказ Тогда
		Результат.СтатусИзвлеченияТекста = Перечисления.СтатусыИзвлеченияТекстаФайлов.Извлечен;
		Результат.ТекстХранилище = Новый ХранилищеЗначения(ИзвлеченныйТекст, Новый СжатиеДанных(9));
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции

// Получает строку из временного хранилища (передача с клиента на сервер,
// делается через временное хранилище).
//
Функция СтрокаИзВременногоХранилища(АдресВременногоХранилищаТекста)
	
	Если ПустаяСтрока(АдресВременногоХранилищаТекста) Тогда
		Возврат "";
	КонецЕсли;
	
	ИмяВременногоФайла = ПолучитьИмяВременногоФайла();
	ДвоичныеДанныеФайла = ПолучитьИзВременногоХранилища(АдресВременногоХранилищаТекста); //ДвоичныеДанные
	ДвоичныеДанныеФайла.Записать(ИмяВременногоФайла);
	
	ТекстовыйФайл = Новый ЧтениеТекста(ИмяВременногоФайла, КодировкаТекста.UTF8);
	Текст = ТекстовыйФайл.Прочитать();
	ТекстовыйФайл.Закрыть();
	
	Попытка
		УдалитьФайлы(ИмяВременногоФайла);
	Исключение
		ЗаписьЖурналаРегистрации(НСтр("ru = 'Файлы.Извлечение текста'",	ОбщегоНазначения.КодОсновногоЯзыка()),
			УровеньЖурналаРегистрации.Ошибка,,,	ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
	КонецПопытки;
	
	Возврат Текст;
	
КонецФункции

// Параметры:
//   ФайлБезТекста - ОпределяемыйТип.ПрисоединенныйФайлОбъект
//
Функция ИзвлечьТекстИзФайла(ФайлБезТекста, ФайлЗаблокирован)
	
	ИмяФайлаСДвоичнымиДанными = "";
	МетаданныеФайла = ФайлБезТекста.Ссылка.Метаданные();
	
	БлокировкаДанных = Новый БлокировкаДанных;
	ЭлементБлокировкиДанных = БлокировкаДанных.Добавить(МетаданныеФайла.ПолноеИмя());
	ЭлементБлокировкиДанных.УстановитьЗначение("Ссылка", ФайлБезТекста.Ссылка);
	
	Если Не ОбщегоНазначения.ЕстьРеквизитОбъекта("ВладелецФайла", МетаданныеФайла) Тогда
		Владелец = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(ФайлБезТекста.Ссылка, "Владелец");
		ЭлементБлокировкиДанных = БлокировкаДанных.Добавить(Владелец.Метаданные().ПолноеИмя());
		ЭлементБлокировкиДанных.УстановитьЗначение("Ссылка", Владелец);
	КонецЕсли;
	
	НачатьТранзакцию();
	Попытка
		
		БлокировкаДанных.Заблокировать();
		ЗаблокироватьДанныеДляРедактирования(ФайлБезТекста.Ссылка, ИмяФайлаСДвоичнымиДанными);
		ФайлЗаблокирован = Истина;
		
		ФайлОбъект = ФайлБезТекста.Ссылка.ПолучитьОбъект();
		ФайлОбъект.ДополнительныеСвойства.Вставить("ИзвлечениеТекста", Истина);
		Если ФайлОбъект <> Неопределено Тогда
			
			Если ЭтоЭлементРаботаСФайлами(ФайлОбъект.Ссылка) Тогда
				МетаданныеОбъекта = Метаданные.НайтиПоТипу(ТипЗнч(ФайлОбъект.Ссылка));
				ВозможностьХранитьВерсии = ОбщегоНазначения.ЕстьРеквизитОбъекта("ТекущаяВерсия", МетаданныеОбъекта);
				Если ВозможностьХранитьВерсии И Не ФайлОбъект.ТекущаяВерсия.Пустая() Тогда
					ИмяФайлаСДвоичнымиДанными = ИмяФайлаСДвоичнымиДанными(ФайлОбъект.ТекущаяВерсия);
				Иначе
					ИмяФайлаСДвоичнымиДанными = ИмяФайлаСДвоичнымиДанными(ФайлОбъект.Ссылка);
				КонецЕсли;
			КонецЕсли;
			
			Если ПустаяСтрока(ИмяФайлаСДвоичнымиДанными) Тогда
				РазблокироватьДанныеДляРедактирования(ФайлБезТекста.Ссылка);
				ФайлЗаблокирован = Ложь;
			Иначе
				РезультатИзвлеченияТекста = ИзвлечьТекстИзФайлаНаДиске(ИмяФайлаСДвоичнымиДанными, ФайлБезТекста.Кодировка);
				ФайлОбъект.СтатусИзвлеченияТекста = РезультатИзвлеченияТекста.СтатусИзвлеченияТекста;
				ФайлОбъект.ТекстХранилище = РезультатИзвлеченияТекста.ТекстХранилище;
				
				ПриЗаписиИзвлеченногоТекста(ФайлОбъект, ФайлЗаблокирован);
				
				Если ФайлОбъект.ТипХраненияФайла <> Перечисления.ТипыХраненияФайлов.ВИнформационнойБазе Тогда
					ИмяФайлаСДвоичнымиДанными = "";
				КонецЕсли;
				
			КонецЕсли;
			
		КонецЕсли;
		
		ЗафиксироватьТранзакцию();
		
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
	Возврат ИмяФайлаСДвоичнымиДанными;
	
КонецФункции

#КонецОбласти

#Область ОбработчикиПодписокНаСобытия

// Обработчик подписки "при записи" версии файла.
//
// Параметры:
//   Источник - СправочникОбъект.ВерсииФайлов
//
Процедура ВерсииФайловПриЗаписи(Источник, Отказ) Экспорт
	
	Если Источник.ОбменДанными.Загрузка Тогда
		ЗаписатьДанныеФайлаВРегистрПриОбмене(Источник);
		Возврат;
	КонецЕсли;
	
	Если Источник.ДополнительныеСвойства.Свойство("ПереименованиеФайла")
		Или Источник.ДополнительныеСвойства.Свойство("КонвертацияФайлов") Тогда
		
		Возврат;
	КонецЕсли;
	
	// Копируем реквизиты из версии в файл.
	ТекущаяВерсия = Источник;
	Если Не ТекущаяВерсия.Ссылка.Пустая() Тогда
	
		ФайлСсылка = Источник.Владелец;
		РеквизитыФайла = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(ФайлСсылка, 
			"ТекущаяВерсия, ИндексКартинки, Размер, ДатаМодификацииУниверсальная, Изменил, Расширение, Том, ПутьКФайлу, ДатаМодификацииУниверсальная");
		
		Если РеквизитыФайла.Размер <> ТекущаяВерсия.Размер 
			ИЛИ РеквизитыФайла.Расширение <> ТекущаяВерсия.Расширение
			ИЛИ РеквизитыФайла.Том <> ТекущаяВерсия.Том
			ИЛИ РеквизитыФайла.ПутьКФайлу <> ТекущаяВерсия.ПутьКФайлу 
			ИЛИ РеквизитыФайла.ИндексКартинки <> ТекущаяВерсия.ИндексКартинки
			ИЛИ РеквизитыФайла.ДатаМодификацииУниверсальная <> ТекущаяВерсия.ДатаМодификацииУниверсальная Тогда
			НачатьТранзакцию();
			Попытка
				БлокировкаДанных = Новый БлокировкаДанных;
				ЭлементБлокировкиДанных = БлокировкаДанных.Добавить(Метаданные.НайтиПоТипу(ТипЗнч(ФайлСсылка)).ПолноеИмя());
				ЭлементБлокировкиДанных.УстановитьЗначение("Ссылка", ФайлСсылка);
				БлокировкаДанных.Заблокировать();
				
				ФайлОбъект = ФайлСсылка.ПолучитьОбъект();
				ФайлОбъект.ИндексКартинки = ТекущаяВерсия.ИндексКартинки;
				ФайлОбъект.Размер = ТекущаяВерсия.Размер;
				ФайлОбъект.Изменил = ТекущаяВерсия.Автор;
				ФайлОбъект.Расширение = ТекущаяВерсия.Расширение;
				ФайлОбъект.Том = ТекущаяВерсия.Том;
				ФайлОбъект.ПутьКФайлу = ТекущаяВерсия.ПутьКФайлу;
				ФайлОбъект.ТипХраненияФайла = ТекущаяВерсия.ТипХраненияФайла;
				ФайлОбъект.ДатаМодификацииУниверсальная = ТекущаяВерсия.ДатаМодификацииУниверсальная;
				Если Не ЗначениеЗаполнено(ФайлОбъект.ДатаСоздания) Тогда
					ФайлОбъект.ДатаСоздания = ТекущаяВерсия.ДатаСоздания;
				КонецЕсли;
				
				Если Источник.ДополнительныеСвойства.Свойство("ЗаписьПодписанногоОбъекта") Тогда
					ФайлОбъект.ДополнительныеСвойства.Вставить("ЗаписьПодписанногоОбъекта",
						Источник.ДополнительныеСвойства.ЗаписьПодписанногоОбъекта);
				КонецЕсли;
				
				// Проверка прав доступа не требуется.
				УстановитьПривилегированныйРежим(Истина);
				ФайлОбъект.Записать();
				УстановитьПривилегированныйРежим(Ложь);
				
				ЗафиксироватьТранзакцию();
			Исключение
				ОтменитьТранзакцию();
				ВызватьИсключение;
			КонецПопытки;
		КонецЕсли;
		
	КонецЕсли;
	
	ОбновитьСостояниеОчередиИзвлеченияТекста(Источник.Ссылка, Источник.СтатусИзвлеченияТекста);
	
КонецПроцедуры

// Обработчик подписки на событие "Перед удалением" присоединенного файла.
Процедура ПередУдалениемПрисоединенногоФайлаСервер(Знач Ссылка,
                                                   Знач ВладелецФайлов,
                                                   Знач Том,
                                                   Знач ТипХраненияФайла,
                                                   Знач ПутьКФайлу) Экспорт
	
	УстановитьПривилегированныйРежим(Истина);
	
	Если ВладелецФайлов <> Неопределено И Не ЕстьФайлыУВладельца(ВладелецФайлов, Ссылка) Тогда
		
		НачатьТранзакцию();
		Попытка
			БлокировкаДанных = Новый БлокировкаДанных;
			ЭлементБлокировкиДанных = БлокировкаДанных.Добавить(Метаданные.РегистрыСведений.НаличиеФайлов.ПолноеИмя());
			ЭлементБлокировкиДанных.УстановитьЗначение("ОбъектСФайлами", ВладелецФайлов);
			БлокировкаДанных.Заблокировать();
			
			МенеджерЗаписи = РегистрыСведений.НаличиеФайлов.СоздатьМенеджерЗаписи();
			МенеджерЗаписи.ОбъектСФайлами = ВладелецФайлов;
			МенеджерЗаписи.Прочитать();
			Если МенеджерЗаписи.Выбран() Тогда
				МенеджерЗаписи.ЕстьФайлы = Ложь;
				МенеджерЗаписи.Записать();
			КонецЕсли;
			
			ЗафиксироватьТранзакцию();
		Исключение
			ОтменитьТранзакцию();
			ВызватьИсключение;
		КонецПопытки;
		
	КонецЕсли;
	
КонецПроцедуры

// Проверяет право текущего пользователя
// при использовании ограничения для папки или файла.
// 
// Параметры:
//  Право        - Строка - имя права.
//  ВладелецПрав - СправочникСсылка.ПапкиФайлов
//               - СправочникСсылка.Файлы
//               - ОпределяемыйТип.ВладелецПрисоединенныхФайлов
//
Функция ЕстьПраво(Право, ВладелецПрав) Экспорт
	
	Если Не ЭтоПапкаФайлов(ВладелецПрав) Тогда
		Возврат Истина; 
	КонецЕсли;
	
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.УправлениеДоступом") Тогда
		МодульУправлениеДоступом = ОбщегоНазначения.ОбщийМодуль("УправлениеДоступом");
		
		Если НЕ МодульУправлениеДоступом.ЕстьПраво(Право, ВладелецПрав) Тогда
			Возврат Ложь;
		КонецЕсли;
	КонецЕсли;
	
	Возврат Истина;
	
КонецФункции

// Обработчик подписки "Обработка проверки заполнения" присоединенного файла.
//
Процедура ОбработкаПроверкиЗаполненияПрисоединенногоФайла(Источник, Отказ) Экспорт
	
	Если Источник.ДополнительныеСвойства.Свойство("ОтложеннаяЗапись")
		И РаботаСФайлами.ДвоичныеДанныеФайла(Источник.Ссылка, Ложь) = Неопределено Тогда
		
		Отказ = Истина;
		
	КонецЕсли;
	
КонецПроцедуры

Функция ЕстьФайлыУВладельца(Знач ВладелецФайлов, Знач ФайлИсключение = Неопределено)
	
	ТекстЗапроса =
	"ВЫБРАТЬ ПЕРВЫЕ 1
	|	ПрисоединенныеФайлы.Ссылка
	|ИЗ
	|	&ИмяСправочника КАК ПрисоединенныеФайлы
	|ГДЕ
	|	НЕ ПрисоединенныеФайлы.ПометкаУдаления
	|	И ПрисоединенныеФайлы.ВладелецФайла = &ВладелецФайлов
	|	И &ФайлИсключение";
	
	Запрос = Новый Запрос;
	Запрос.Параметры.Вставить("ВладелецФайлов", ВладелецФайлов);
	Если ФайлИсключение <> Неопределено Тогда
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ФайлИсключение", "ПрисоединенныеФайлы.Ссылка <> &ФайлИсключение"); // @Query-part-2
		Запрос.Параметры.Вставить("ФайлИсключение", ФайлИсключение);
	Иначе
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ФайлИсключение", "ИСТИНА"); // @Query-part-2
	КонецЕсли;
	
	ТекстыЗапроса = Новый Массив;	
	Для каждого КлючИЗначение Из ИменаСправочниковХраненияФайлов(ВладелецФайлов) Цикл
		ТекстыЗапроса.Добавить(СтрЗаменить(ТекстЗапроса, "&ИмяСправочника", "Справочник." + КлючИЗначение.Ключ));
	КонецЦикла;
	
	Запрос.Текст = СтрСоединить(ТекстыЗапроса, Символы.ПС + "ОБЪЕДИНИТЬ" + Символы.ПС);
	УстановитьПривилегированныйРежим(Истина);
	Возврат НЕ Запрос.Выполнить().Пустой();
	
КонецФункции

#КонецОбласти

#Область ОбновлениеИнформационнойБазы

Процедура ОбновитьСписокЗапрещенныхРасширений() Экспорт
	
	СписокЗапрещенныхРасширенийДляЗагрузки = СписокЗапрещенныхРасширений();
	
	СписокЗапрещенныхРасширенийВБазе = Константы.СписокЗапрещенныхРасширений.Получить();
	МассивЗапрещенныхРасширений = СтрРазделить(СписокЗапрещенныхРасширенийВБазе, " ");
	ОбновитьСписокЗапрещенныхРасширений = Ложь;
	Для Каждого Расширение Из СписокЗапрещенныхРасширенийДляЗагрузки Цикл
		Если МассивЗапрещенныхРасширений.Найти(ВРег(Расширение)) = Неопределено Тогда
			ОбновитьСписокЗапрещенныхРасширений = Истина;
			МассивЗапрещенныхРасширений.Добавить(ВРег(Расширение));
		КонецЕсли;
	КонецЦикла;
	СписокЗапрещенныхРасширенийВБазе = СтрСоединить(МассивЗапрещенныхРасширений, " ");
	Если ОбновитьСписокЗапрещенныхРасширений Тогда
		Константы.СписокЗапрещенныхРасширений.Установить(СписокЗапрещенныхРасширенийВБазе);
	КонецЕсли;
	
КонецПроцедуры

Процедура ОбновитьСписокЗапрещенныхРасширенийВОбластиДанных() Экспорт
	
	СписокЗапрещенныхРасширенийДляЗагрузки = СписокЗапрещенныхРасширений();
	
	ОбновитьСписокЗапрещенныхРасширенийОбластиДанных = Ложь;
	СписокЗапрещенныхРасширенийОбластиДанных = Константы.СписокЗапрещенныхРасширенийОбластиДанных.Получить();
	МассивЗапрещенныхРасширенийОбластиДанных = СтрРазделить(СписокЗапрещенныхРасширенийОбластиДанных, " ");
	Для Каждого Расширение Из СписокЗапрещенныхРасширенийДляЗагрузки Цикл
		Если МассивЗапрещенныхРасширенийОбластиДанных.Найти(ВРег(Расширение)) = Неопределено Тогда
			МассивЗапрещенныхРасширенийОбластиДанных.Добавить(ВРег(Расширение));
			ОбновитьСписокЗапрещенныхРасширенийОбластиДанных = Истина;
		КонецЕсли;
	КонецЦикла;
	СписокЗапрещенныхРасширенийОбластиДанных = СтрСоединить(МассивЗапрещенныхРасширенийОбластиДанных, " ");
	Если ОбновитьСписокЗапрещенныхРасширенийОбластиДанных Тогда
		Константы.СписокЗапрещенныхРасширенийОбластиДанных.Установить(СписокЗапрещенныхРасширенийОбластиДанных);
	КонецЕсли;
	
КонецПроцедуры

Процедура ОбновитьСписокРасширенийТекстовыхФайлов() Экспорт
	
	СписокРасширений = Константы.СписокРасширенийТекстовыхФайлов.Получить();
	РасширенияТекстовыхФайлов = СтрРазделить(СписокРасширений, " ");
	
	ОбновитьСписокРасширений = Ложь;
	Для Каждого Расширение Из СтрРазделить(СписокРасширенийТекстовыхФайлов(), " ") Цикл
		Если РасширенияТекстовыхФайлов.Найти(ВРег(Расширение)) = Неопределено Тогда
			ОбновитьСписокРасширений = Истина;
			РасширенияТекстовыхФайлов.Добавить(ВРег(Расширение));
		КонецЕсли;
	КонецЦикла;
	Если ОбновитьСписокРасширений Тогда
		Константы.СписокРасширенийТекстовыхФайлов.Установить(СтрСоединить(РасширенияТекстовыхФайлов, " "));
	КонецЕсли;
	
КонецПроцедуры

// Удаляет запись в регистре ХранимыеФайлыВерсий.
//
// Параметры:
//   Файл - ОпределяемыйТип.ПрисоединенныйФайл - ссылка на файл.
//
Процедура УдалитьЗаписьИзРегистраДвоичныеДанныеФайлов(Файл) Экспорт
	
	УстановитьПривилегированныйРежим(Истина);
	
	НаборЗаписей = РегистрыСведений.ДвоичныеДанныеФайлов.СоздатьНаборЗаписей();
	НаборЗаписей.Отбор.Файл.Установить(Файл);
	НаборЗаписей.Записать();
	
КонецПроцедуры

Функция КоличествоВерсийСНеизвлеченнымТекстом() Экспорт
	
	ТекстыЗапроса = Новый Массив;	
	Для Каждого Тип Из Метаданные.ОпределяемыеТипы.ПрисоединенныйФайл.Тип.Типы() Цикл
		
		ТекстЗапроса = 
			"ВЫБРАТЬ
			|	ЕСТЬNULL(КОЛИЧЕСТВО(Файлы.Ссылка), 0) КАК КоличествоФайлов
			|ИЗ
			|	&ИмяСправочника КАК Файлы
			|ГДЕ
			|	Файлы.СтатусИзвлеченияТекста В (ЗНАЧЕНИЕ(Перечисление.СтатусыИзвлеченияТекстаФайлов.НеИзвлечен), 
			|		ЗНАЧЕНИЕ(Перечисление.СтатусыИзвлеченияТекстаФайлов.ПустаяСсылка))";
	
		Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ЭлектроннаяПодпись") Тогда
			Если Тип = Тип("СправочникСсылка.ВерсииФайлов") Тогда
				ТекстЗапроса = ТекстЗапроса + "
					|	И НЕ Файлы.Владелец.Зашифрован";
			Иначе
				ТекстЗапроса = ТекстЗапроса + "
					|	И НЕ Файлы.Зашифрован";
			КонецЕсли;
		КонецЕсли;
	
		МетаданныеСправочникаФайлов = Метаданные.НайтиПоТипу(Тип);
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ИмяСправочника", "Справочник." + МетаданныеСправочникаФайлов.Имя);
		ТекстыЗапроса.Добавить(ТекстЗапроса);
		
	КонецЦикла;
	
	КоличествоФайлов = 0;	
	Запрос = Новый Запрос(СтрСоединить(ТекстыЗапроса, Символы.ПС + "ОБЪЕДИНИТЬ" + Символы.ПС));
	Выборка = Запрос.Выполнить().Выбрать();
	Если Выборка.Следующий() Тогда
		КоличествоФайлов = КоличествоФайлов + Выборка.КоличествоФайлов;
	КонецЕсли;
	
	Возврат КоличествоФайлов;
	
КонецФункции

// Регистрирует на плане обмена ОбновлениеИнформационнойБазы объекты,
// для которых необходимо обновить записи в реестре.
//
Процедура ЗарегистрироватьОбъектыДляПереносаЭлектронныхПодписейИСертификатовШифрования(Параметры) Экспорт
	
	ТекстЗапросаДвеТабличныеЧасти =
	"ВЫБРАТЬ
	|	Файлы.Ссылка КАК Ссылка
	|ИЗ
	|	ТаблицаОбъектов КАК Файлы
	|ГДЕ
	|	(ИСТИНА В
	|				(ВЫБРАТЬ ПЕРВЫЕ 1
	|					ИСТИНА
	|				ИЗ
	|					ТабличнаяЧастьУдалитьСертификатыШифрования КАК УдалитьСертификатыШифрования
	|				ГДЕ
	|					УдалитьСертификатыШифрования.Ссылка = Файлы.Ссылка)
	|			ИЛИ ИСТИНА В
	|				(ВЫБРАТЬ ПЕРВЫЕ 1
	|					ИСТИНА
	|				ИЗ
	|					ТабличнаяЧастьУдалитьЭлектронныеПодписи КАК УдалитьЭлектронныеПодписи
	|				ГДЕ
	|					УдалитьЭлектронныеПодписи.Ссылка = Файлы.Ссылка))";
	
	ТекстЗапросаТЧУдалитьСертификатыШифрования =
	"ВЫБРАТЬ
	|	Файлы.Ссылка КАК Ссылка
	|ИЗ
	|	ТаблицаОбъектов КАК Файлы
	|ГДЕ
	|	ИСТИНА В
	|			(ВЫБРАТЬ ПЕРВЫЕ 1
	|				ИСТИНА
	|			ИЗ
	|				ТабличнаяЧастьУдалитьСертификатыШифрования КАК УдалитьСертификатыШифрования
	|			ГДЕ
	|				УдалитьСертификатыШифрования.Ссылка = Файлы.Ссылка)";
	
	ТекстЗапросаТЧУдалитьЭлектронныеПодписи =
	"ВЫБРАТЬ
	|	Файлы.Ссылка КАК Ссылка
	|ИЗ
	|	ТаблицаОбъектов КАК Файлы
	|ГДЕ
	|	ИСТИНА В
	|			(ВЫБРАТЬ ПЕРВЫЕ 1
	|				ИСТИНА
	|			ИЗ
	|				ТабличнаяЧастьУдалитьЭлектронныеПодписи КАК УдалитьЭлектронныеПодписи
	|			ГДЕ
	|				УдалитьЭлектронныеПодписи.Ссылка = Файлы.Ссылка)";
	
	Запрос = Новый Запрос;
	Для Каждого ПолноеИмя Из ПолныеИменаСправочниковПрисоединенныхФайлов() Цикл
		ОбъектМетаданных = ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(ПолноеИмя);
		
		ЕстьТЧУдалитьСертификатыШифрования =
			ОбъектМетаданных.ТабличныеЧасти.Найти("УдалитьСертификатыШифрования") <> Неопределено;
		
		ЕстьТЧУдалитьЭлектронныеПодписи =
			ОбъектМетаданных.ТабличныеЧасти.Найти("УдалитьЭлектронныеПодписи") <> Неопределено;
		
		Если ЕстьТЧУдалитьСертификатыШифрования И ЕстьТЧУдалитьЭлектронныеПодписи Тогда
			ТекущийТекстЗапроса = ТекстЗапросаДвеТабличныеЧасти;
			
		ИначеЕсли ЕстьТЧУдалитьСертификатыШифрования Тогда
			ТекущийТекстЗапроса = ТекстЗапросаТЧУдалитьСертификатыШифрования;
			
		ИначеЕсли ЕстьТЧУдалитьЭлектронныеПодписи Тогда
			ТекущийТекстЗапроса = ТекстЗапросаТЧУдалитьЭлектронныеПодписи;
		Иначе 
			Продолжить;
		КонецЕсли;
		
		Если ЗначениеЗаполнено(Запрос.Текст) Тогда
			Запрос.Текст = Запрос.Текст + "
			|
			|ОБЪЕДИНИТЬ ВСЕ
			|
			|";
		КонецЕсли;
		
		ТекущийТекстЗапроса = СтрЗаменить(ТекущийТекстЗапроса, "ТаблицаОбъектов", ПолноеИмя);
		
		ТекущийТекстЗапроса = СтрЗаменить(ТекущийТекстЗапроса,
			"ТабличнаяЧастьУдалитьСертификатыШифрования", ПолноеИмя + ".УдалитьСертификатыШифрования");
		
		ТекущийТекстЗапроса = СтрЗаменить(ТекущийТекстЗапроса,
			"ТабличнаяЧастьУдалитьЭлектронныеПодписи", ПолноеИмя + ".УдалитьЭлектронныеПодписи");
		
		Запрос.Текст = Запрос.Текст + ТекущийТекстЗапроса;
	КонецЦикла;
	
	МассивСсылок = Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Ссылка"); 
	ОбновлениеИнформационнойБазы.ОтметитьКОбработке(Параметры, МассивСсылок);
	
КонецПроцедуры

Процедура ПеренестиЭлектронныеПодписиИСертификатыШифрованияВРегистрыСведений(Параметры) Экспорт
	
	ОбработкаЗавершена = Истина;
	Для Каждого ПолноеИмяСправочника Из ПолныеИменаСправочниковПрисоединенныхФайлов() Цикл
		// @skip-check query-in-loop - Порционная обработка больших объемов данных.
		ПеренестиЭлектронныеПодписиИСертификатыШифрованияВРегистрыСведенийДляТаблицы(Параметры,
			ПолноеИмяСправочника, ОбработкаЗавершена);
	КонецЦикла;
	Параметры.ОбработкаЗавершена = ОбработкаЗавершена;
	
КонецПроцедуры

// Позволяет перенести элементы табличных частей УдалитьЭлектронныеПодписи и УдалитьСертификатыШифрования
// в регистры сведений ЭлектронныеПодписи и СертификатыШифрования.
//
// Параметры:
//  ПараметрыОбновления        - Структура - структура параметров отложенного обработчика обновления.
//
//  ПолноеИмяОбъектаМетаданных - Строка - полное имя объекта метаданных, из которого переносятся данные табличных частей
//                                        УдалитьЭлектронныеПодписи и УдалитьСертификатыШифрования.
//  ОбработкаЗавершена         - Булево - Истина, если обработаны все данные при обновлении ИБ.
//
Процедура ПеренестиЭлектронныеПодписиИСертификатыШифрованияВРегистрыСведенийДляТаблицы(ПараметрыОбновления, 
	ПолноеИмяОбъектаМетаданных, ОбработкаЗавершена)
	
	ОбъектМетаданных = ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(ПолноеИмяОбъектаМетаданных);
	
	Если ОбъектМетаданных = Неопределено Тогда
		ВызватьИсключение НСтр("ru = 'Не указан объект для обработки электронных подписей и сертификатов шифрования.'");
	КонецЕсли;
	
	ЕстьТабличнаяЧастьЭП = ОбъектМетаданных.ТабличныеЧасти.Найти("УдалитьЭлектронныеПодписи") <> Неопределено;
	ЕстьТабличнаяЧастьСШ = ОбъектМетаданных.ТабличныеЧасти.Найти("УдалитьСертификатыШифрования") <> Неопределено;
	
	ВыборкаСсылок = ОбновлениеИнформационнойБазы.ВыбратьСсылкиДляОбработки(ПараметрыОбновления.Очередь, ПолноеИмяОбъектаМетаданных);
	
	ОбъектовОбработано = 0;
	ПроблемныхОбъектов = 0;
	
	МассивСсылок = Новый Массив;
	
	НачатьТранзакцию();
	Попытка
		Пока ВыборкаСсылок.Следующий() Цикл
			МассивСсылок.Добавить(ВыборкаСсылок.Ссылка);
		КонецЦикла;
		
		Если ЕстьТабличнаяЧастьЭП Тогда
			ПеренестиДанныеЭлектроннойПодписиВРегистрСведений(МассивСсылок,
				ПолноеИмяОбъектаМетаданных, ОбъектМетаданных);
		КонецЕсли;
		
		Если ЕстьТабличнаяЧастьСШ Тогда
			ПеренестиДанныеСертификатовВРегистрСведений(МассивСсылок, ПолноеИмяОбъектаМетаданных);
		КонецЕсли;
		
		Для Каждого ОбъектСЭП Из МассивСсылок Цикл
			ОбновлениеИнформационнойБазы.ОтметитьВыполнениеОбработки(ОбъектСЭП);
		КонецЦикла;
		ОбъектовОбработано = МассивСсылок.Количество();
		
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		// Если не удалось обработать какой-либо объект, требуется повторить попытку.
		ПроблемныхОбъектов = ПроблемныхОбъектов + МассивСсылок.Количество();
		
		ТекстСообщения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Не удалось обработать объект %1 по причине:
			           |%2'"),
			ОбъектМетаданных,
			ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
		
		ЗаписьЖурналаРегистрации(ОбновлениеИнформационнойБазы.СобытиеЖурналаРегистрации(),
			УровеньЖурналаРегистрации.Предупреждение, ОбъектМетаданных, , ТекстСообщения);
	КонецПопытки;
	
	Если Не ОбновлениеИнформационнойБазы.ОбработкаДанныхЗавершена(ПараметрыОбновления.Очередь, ПолноеИмяОбъектаМетаданных) Тогда
		ОбработкаЗавершена = Ложь;
	КонецЕсли;
	
	Если ОбъектовОбработано = 0 И ПроблемныхОбъектов <> 0 Тогда
		ТекстСообщения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Не удалось обработать некоторые объекты (пропущены): %1'"),
			ПроблемныхОбъектов);
		ВызватьИсключение ТекстСообщения;
	Иначе
		ЗаписьЖурналаРегистрации(ОбновлениеИнформационнойБазы.СобытиеЖурналаРегистрации(),
			УровеньЖурналаРегистрации.Информация,
			ОбъектМетаданных,
			,
			СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Обработана очередная порция объектов: %1'"),
				ОбъектовОбработано));
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры ПеренестиЭлектронныеПодписиИСертификатыШифрованияВРегистрыСведенийДляТаблицы.
Процедура ПеренестиДанныеЭлектроннойПодписиВРегистрСведений(МассивОбъектов, ПолноеИмяОбъектаМетаданных, ОбъектМетаданных)
	
	УстановитьПривилегированныйРежим(Истина);
	
	Блокировка = Новый БлокировкаДанных;
	Для Каждого ОбъектСДанными Из МассивОбъектов Цикл
		ЭлементБлокировки = Блокировка.Добавить(ПолноеИмяОбъектаМетаданных);
		ЭлементБлокировки.УстановитьЗначение("Ссылка", ОбъектСДанными);
	КонецЦикла;
	
	НачатьТранзакцию();
	Попытка
		
		Блокировка.Заблокировать();
		
		Запрос = Новый Запрос;
		Запрос.Текст =
		"ВЫБРАТЬ
		|	ТЧЭлектронныеПодписи.Ссылка КАК ПодписанныйОбъект,
		|	ТЧЭлектронныеПодписи.ДатаПодписи,
		|	ТЧЭлектронныеПодписи.ИмяФайлаПодписи,
		|	ТЧЭлектронныеПодписи.Комментарий,
		|	ТЧЭлектронныеПодписи.КомуВыданСертификат,
		|	ТЧЭлектронныеПодписи.Отпечаток,
		|	ТЧЭлектронныеПодписи.Подпись,
		|	ТЧЭлектронныеПодписи.УстановившийПодпись,
		|	ТЧЭлектронныеПодписи.НомерСтроки КАК ПорядковыйНомер,
		|	ТЧЭлектронныеПодписи.Сертификат, 
		|	ТЧЭлектронныеПодписи.ПодписьВерна КАК ПодписьВерна,
		|	ТЧЭлектронныеПодписи.ДатаПроверкиПодписи КАК ДатаПроверкиПодписи
		|ИЗ
		|	&УдалитьЭлектронныеПодписи КАК ТЧЭлектронныеПодписи
		|ГДЕ
		|	ТЧЭлектронныеПодписи.Ссылка В(&МассивОбъектов)
		|ИТОГИ
		|	ПО ПодписанныйОбъект";
		
		Запрос.Текст = СтрЗаменить(Запрос.Текст, "&УдалитьЭлектронныеПодписи", ПолноеИмяОбъектаМетаданных + ".УдалитьЭлектронныеПодписи"); 
		Если ОбъектМетаданных = Метаданные.Справочники.ВерсииФайлов Тогда
			Запрос.Текст = СтрЗаменить(Запрос.Текст,
				"ТЧЭлектронныеПодписи.Ссылка КАК ПодписанныйОбъект",
				"ТЧЭлектронныеПодписи.Ссылка.Владелец КАК ПодписанныйОбъект");
		КонецЕсли;
		
		РеквизитыТЧ = ОбъектМетаданных.ТабличныеЧасти.УдалитьЭлектронныеПодписи.Реквизиты;
		
		Если РеквизитыТЧ.Найти("ПодписьВерна") = Неопределено Тогда
			Запрос.Текст = СтрЗаменить(Запрос.Текст, "ТЧЭлектронныеПодписи.ПодписьВерна", "ЛОЖЬ");
		КонецЕсли;
		
		Если РеквизитыТЧ.Найти("ДатаПроверкиПодписи") = Неопределено Тогда
			Запрос.Текст = СтрЗаменить(Запрос.Текст, "ТЧЭлектронныеПодписи.ДатаПроверкиПодписи", "Неопределено");
		КонецЕсли;
		
		Запрос.УстановитьПараметр("МассивОбъектов", МассивОбъектов);
		Выгрузка = Запрос.Выполнить().Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам);
		
		Для Каждого Строка Из Выгрузка.Строки Цикл
			Если Не ЗначениеЗаполнено(Строка.ПодписанныйОбъект) Тогда
				Продолжить;
			КонецЕсли;
			НаборЗаписей = РегистрыСведений["ЭлектронныеПодписи"].СоздатьНаборЗаписей(); // РегистрСведенийНаборЗаписей
			НаборЗаписей.Отбор.ПодписанныйОбъект.Установить(Строка.ПодписанныйОбъект);
			Для Каждого Подстрока Из Строка.Строки Цикл
				ЗаполнитьЗначенияСвойств(НаборЗаписей.Добавить(), Подстрока);
			КонецЦикла;
			НаборЗаписей.ОбменДанными.Загрузка = Истина;
			НаборЗаписей.ДополнительныеСвойства.Вставить("ОтключитьМеханизмРегистрацииОбъектов");
			НаборЗаписей.ОбменДанными.Получатели.АвтоЗаполнение = Ложь;
			НаборЗаписей.Записать();
		КонецЦикла;
		
		ЗафиксироватьТранзакцию();
		
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

// Для процедуры ПеренестиЭлектронныеПодписиИСертификатыШифрованияВРегистрыСведенийДляТаблицы.
Процедура ПеренестиДанныеСертификатовВРегистрСведений(МассивОбъектов, ПолноеИмяОбъектаМетаданных)
	
	УстановитьПривилегированныйРежим(Истина);
	
	Блокировка = Новый БлокировкаДанных;
	Для Каждого ОбъектСДанными Из МассивОбъектов Цикл
		ЭлементБлокировки = Блокировка.Добавить(ПолноеИмяОбъектаМетаданных);
		ЭлементБлокировки.УстановитьЗначение("Ссылка", ОбъектСДанными);
	КонецЦикла;
	
	НачатьТранзакцию();
	Попытка
		
		Блокировка.Заблокировать();
		
		Запрос = Новый Запрос;
		Запрос.Текст =
		"ВЫБРАТЬ
		|	ТЧСертификатыШифрования.Ссылка КАК ЗашифрованныйОбъект,
		|	ТЧСертификатыШифрования.Отпечаток,
		|	ТЧСертификатыШифрования.Сертификат,
		|	ТЧСертификатыШифрования.НомерСтроки КАК ПорядковыйНомер,
		|	ТЧСертификатыШифрования.Представление
		|ИЗ
		|	&УдалитьСертификатыШифрования КАК ТЧСертификатыШифрования
		|ГДЕ
		|	ТЧСертификатыШифрования.Ссылка В(&МассивОбъектов)
		|ИТОГИ
		|	ПО ЗашифрованныйОбъект";
		
		Запрос.Текст = СтрЗаменить(Запрос.Текст, "&УдалитьСертификатыШифрования", 
			ПолноеИмяОбъектаМетаданных + ".УдалитьСертификатыШифрования");
		Запрос.УстановитьПараметр("МассивОбъектов", МассивОбъектов);
		
		Выгрузка = Запрос.Выполнить().Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам);
		
		Для Каждого Строка Из Выгрузка.Строки Цикл
			НаборЗаписей = РегистрыСведений["СертификатыШифрования"].СоздатьНаборЗаписей(); // РегистрСведенийНаборЗаписей
			НаборЗаписей.Отбор.ЗашифрованныйОбъект.Установить(Строка.ЗашифрованныйОбъект);
			Для Каждого Подстрока Из Строка.Строки Цикл
				ЗаполнитьЗначенияСвойств(НаборЗаписей.Добавить(), Подстрока);
			КонецЦикла;
			НаборЗаписей.ОбменДанными.Загрузка = Истина;
			НаборЗаписей.ДополнительныеСвойства.Вставить("ОтключитьМеханизмРегистрацииОбъектов");
			НаборЗаписей.ОбменДанными.Получатели.АвтоЗаполнение = Ложь;
			НаборЗаписей.Записать();
		КонецЦикла;
	
		ЗафиксироватьТранзакцию();
		
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

Функция ПолныеИменаСправочниковПрисоединенныхФайлов()
	
	Массив = Новый Массив;
	
	ТипыПрисоединенногоФайла = ОбщегоНазначения.ИдентификаторыОбъектовМетаданных(Метаданные.ОпределяемыеТипы.ПрисоединенныйФайл.Тип.Типы());
	
	Для Каждого ТипПрисоединенногоФайла Из ТипыПрисоединенногоФайла Цикл
		Массив.Добавить(ТипПрисоединенногоФайла.Значение.ПолноеИмя);
	КонецЦикла;
	
	Если Массив.Найти("Справочник.Файлы") = Неопределено Тогда
		Массив.Добавить("Справочник.Файлы");
	КонецЕсли;
	
	Если Массив.Найти("Справочник.ВерсииФайлов") = Неопределено Тогда
		Массив.Добавить("Справочник.ВерсииФайлов");
	КонецЕсли;
	
	Возврат Массив;
	
КонецФункции

Функция ИзменяемыеОбъектыПриПереносеЭлектронныхПодписейИСертификатовШифрования()
	
	Возврат Метаданные.РегистрыСведений["ЭлектронныеПодписи"].ПолноеИмя() + ", "
	      + Метаданные.РегистрыСведений["СертификатыШифрования"].ПолноеИмя();
	
КонецФункции

#КонецОбласти

#КонецОбласти